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
  • 相关阅读:
    Code基础——2.排序
    设计模式——4.装饰模式
    Shader笔记——1.光照基础
    C#笔记——7.序列化与反序列化
    C#笔记——6.反射与特性
    lua小技巧记录--新建对象时重置元表
    发现的lua小技巧记录--在:方法中使用self的技巧
    lua版pureMVC框架使用分析
    在xlua中使用DoTween动画插件
    Unity工程性能优化学习笔记
  • 原文地址:https://www.cnblogs.com/hhhshct/p/11442011.html
Copyright © 2011-2022 走看看