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

Posted hhhshct

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手动实现自己的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

 

以上是关于手动实现自己的spring事务注解的主要内容,如果未能解决你的问题,请参考以下文章

如何手动开启spring事务

spring事务bug

Spring Boot事务和事务传播机制

Spring事务管理那些事

Spring Boot事务和事务传播机制

Spring基础(十六):Spring事务管理注解方式和XML配置方式