zoukankan      html  css  js  c++  java
  • 手动实现自己的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
  • 相关阅读:
    PHP命令行脚本接收传入参数的三种方式
    php中include()和require()的区别
    自定义一个可复用的BaseAdapter
    Java RandomAccessFile用法
    java中两字符串比较--compareTo方法
    机房内网电脑时间自动同步器
    Java中对JSONArray中的对象的某个字段进行排序
    jquery一个元素绑定了多个 click 事件,如何取消其中的一个
    js和Java分别正则匹配小数点后多余的0
    javascript 去掉小数末尾多余的零
  • 原文地址:https://www.cnblogs.com/hhhshct/p/11442011.html
Copyright © 2011-2022 走看看