君临-行者无界

导航

手动实现自己的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

 

posted on 2019-09-01 12:56  请叫我西毒  阅读(1402)  评论(0编辑  收藏  举报