手动实现自己的spring事务注解

  spring事务是基于同一个数据连接来实现的,认识到这一点是spring事务的关键,spring事务的关键点便在于在事务中不管执行几次db操作,始终使用的是同一个数据库连接。通过查看源码,我们可以看到spring事务实现思路如下

  这其中的关键点就在于如何保证在事务内获取的数据库连接为同一个以及通过aop来代理数据库连接的提交、回滚。代码如下

  构建自己的事务管理器,使用threadlocal来保证一个线程内获取到的数据库连接为同一个

package com.jlwj.custom;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by Administrator on 2019/8/31.
 */
@Component
public class MyTransactionManager {

    private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;

    public Connection getConnection(){
        Connection connection = null;
        if(threadLocal.get()!=null){
            connection = threadLocal.get();
        }else{
            try {
                connection =  dataSource.getConnection();
                threadLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
        return  connection;
    }

}

  自定义注解

package com.jlwj.custom;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by Administrator on 2019/8/31.
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactionAnnotation {
}

  自定义jdbcTemplate简化db操作

package com.jlwj.custom;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * Created by Administrator on 2019/8/31.
 */
@Component
public class MyJdbcTemplate {
    @Autowired
    private MyTransactionManager transactionManager;

    public void execute(String sql,@Nullable Object... args) throws SQLException {
        Connection connection = transactionManager.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            if(args[i] instanceof Integer){
                preparedStatement.setInt(i+1, (Integer) args[i]);
            }else if(args[i] instanceof String){
                preparedStatement.setString(i+1, (String) args[i]);
            }
        }
        preparedStatement.execute();

    }
}

  aop切面,对添加了我们自定义注解的方法进行增强,开启事务

package com.jlwj.custom;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by Administrator on 2019/8/31.
 */
@Aspect
@Component
public class Aop {

    @Autowired
    private MyTransactionManager myTransactionManager;

    @Around("@annotation(com.jlwj.custom.MyTransactionAnnotation)")
    public Object doTransaction(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object o = null;
        Connection connection = myTransactionManager.getConnection();
        try {
            connection.setAutoCommit(false);
            o = proceedingJoinPoint.proceed();
            connection.commit();

        } catch (SQLException e) {
            e.printStackTrace();
            connection.rollback();
        }finally {
            connection.close();
        }
        return o;
    }
}

  测试service及测试类

package com.jlwj.service;

import com.jlwj.custom.MyJdbcTemplate;
import com.jlwj.custom.MyTransactionAnnotation;
import com.jlwj.custom.MyTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Created by Administrator on 2019/8/31.
 */
@Service
public class TransactionTestService2 {

    @Autowired
    private MyJdbcTemplate myJdbcTemplate;

    @Autowired
    private MyTransactionManager transactionManager;

    @MyTransactionAnnotation
    public void addUser(String userName,Integer userId){
        String sql1 = "insert into t_user(user_id,user_name,password,phone) values(?,?,?,?)";
        String sql2 = "insert into t_log(content)values (?)";
        try {
            myJdbcTemplate.execute(sql1,userId,userName,"1231456","123213213123");
            myJdbcTemplate.execute(sql2,userName);
//            int a = 1/0;
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    public void addUser2(String userName,Integer userId){
        String sql1 = "insert into t_user(user_id,user_name,password,phone) values(?,?,?,?)";
        String sql2 = "insert into t_log(content)values (?)";
        try {
            myJdbcTemplate.execute(sql1,userId,userName,"1231456","123213213123");
            myJdbcTemplate.execute(sql2,userName);
            int a = 1/0;
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

}
package com.jlwj;

import com.jlwj.service.TransactionTestService;
import com.jlwj.service.TransactionTestService2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomTransactionApplicationTests {

    @Autowired
    private TransactionTestService transactionTestService;

    @Autowired
    private TransactionTestService2 transactionTestService2;

    @Test
    public void contextLoads() {
        transactionTestService.addUser("wangwu",3);
    }

    @Test
    public void contextLoad2() {
        transactionTestService2.addUser("qwe",7);
    }

    @Test
    public void contextLoad3() {
        transactionTestService2.addUser2("123",8);
    }

}

  为了方便,构建了一个spingboot项目,依赖和配置如下

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
spring:
  datasource:
    druid:
      url: jdbc:mysql://127.0.0.1:3306/test02?characterEncoding=utf-8&serverTimezone=UTC
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      initial-size: 1
      max-active: 20
      max-wait: 6000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
      min-idle: 1
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false

原文地址:https://www.cnblogs.com/hhhshct/p/11442011.html

时间: 2024-08-28 23:49:52

手动实现自己的spring事务注解的相关文章

spring 事务注解

Spring事务传播行为类型 事务传播行为类型 说明 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中.这是最常见的选择. PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行. PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常. PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起. PROPAGATION_

23. Spring 事务注解@Transactional和异常捕获

一. 事务注解限制条件 1. 不允许在private方法上面 2. 不能在非事务方法里面调用事务方法 二. 实现机制-异常捕获 Describes transaction attributes on a method or class. This annotation type is generally directly comparable to Spring's org.springframework.transaction.interceptor.RuleBasedTransactionA

Spring事务注解@Transactional失效的问题

在项目中发现事务失效,使用@Transactional注解标注的Service业务层实现类方法全部不能回滚事务了,最终发现使用因为Spring与shiro进行整合之后导致的问题,将所有的Service层实现类都添加如下注解 @Scope(proxyMode= ScopedProxyMode.TARGET_CLASS) 将代理方式换成CGLib的代理方式之后得以解决,最终不明原因,如有看到这篇博客并知道答案的朋友请留言告知 如果事务不能回滚,也需要考虑如下几点: 表得存储引擎为MyISAM是没有事

Spring事务注解

使用步骤: 步骤一.在spring配置文件中引入<tx:>命名空间 <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:tx="http://www.springframework.org/schema/tx"  xsi:schemaLoca

Spring 事务注解 错误问题

碰到个问题,在一个springmvc项目中,设置好事务,然后在service上添加@Transactional注解,只要一添加这个注解,该service就无法被spring创建成功, error creating bean with name xxx什么的.搞了半天,发先service写成Interface加Implement的方式就可以了,也就是Service要写接口和实现. 具体原理不清楚,估计是动态代理的问题.有谁了解详情的话可以在评论里告知一下.

Spring 事务注解@Transactional

事务管理一般有编程式和声明式两种,编程式是直接在代码中进行编写事物处理过程,而声名式则是通过注解方式或者是在xml文件中进行配置,相对编程式很方便. 而注解方式通过@Transactional 是常见的.我们可以使用@EnableTransactionManagement 注解来启用事务管理功能,该注解可以加在启动类上或者单独加个配置类来处理. 1.Transactional 注解的属性 name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器

【spring 事务注解配置】事务回滚

注解配置参考: http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html 自己测试发现事务没成功,原因是 我测试的表是myisam 存储引擎.myisam不支持事务,一个坑 了我! myisam 和 innodb对比 http://www.cnblogs.com/merlini/p/4079313.html

Spring 之注解事务 @Transactional

众所周知的ACID属性:  原子性(atomicity).一致性(consistency).隔离性(isolation)以及持久性(durability).我们无法控制一致性.原子性以及持久性,但可以控制超时,设置事务的只读性以指定隔离级别.  Spring在TransactionDefinition接口封装了所有这些设置. Spring在TransactionDefinition接口中规定了7种类型的事务传播行为, 它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播: 事务传播行为类型

深入理解 Spring 事务原理

一.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的.对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获取连接 Connection con = DriverManager.getConnection() 开启事务con.setAutoCommit(true/false); 执行CRUD 提交事务/回滚事务 con.commit() / con.rollback(); 关闭连接 conn.close(); 使