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事务注解的更多相关文章

  1. 关于spring事务注解实战

    1.概述 spring的事务注解@Transaction 相信很多人都用过,而@Transaction 默认配置适合80%的配置. 本篇文章不是对spring注解事务做详细介绍,而是解决一些实际场景下 ...

  2. Spring事务注解分析

    1.使用spring事务注解 2.手写事务注解 1).sql执行器 2).事务注解定义 3).AOP实现事务具体实现(同一个线程中使用同一个连接) 4).应用使用注解前 5).应用使用注解后

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

    在项目中发现事务失效,使用@Transactional注解标注的Service业务层实现类方法全部不能回滚事务了,最终发现使用因为Spring与shiro进行整合之后导致的问题,将所有的Service ...

  4. Spring事务管理者与Spring事务注解--声明式事务

    1.在Spring的applicationContext.xml中配置事务管理者 PS:具体的说明请看代码中的注释 Xml代码: <!-- 声明式事务管理的配置 --> <!-- 添 ...

  5. spring事务注解

    @Transactional只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能. Spring使用声明式事务处理,默认 ...

  6. 这一次搞懂Spring事务注解的解析

    前言 事务我们都知道是什么,而Spring事务就是在数据库之上利用AOP提供声明式事务和编程式事务帮助我们简化开发,解耦业务逻辑和系统逻辑.但是Spring事务原理是怎样?事务在方法间是如何传播的?为 ...

  7. Spring 事务注解 错误问题

    碰到个问题,在一个springmvc项目中,设置好事务,然后在service上添加@Transactional注解,只要一添加这个注解,该service就无法被spring创建成功, error cr ...

  8. Spring事务注解@Transactional回滚问题

    Spring配置文件,声明事务时,如果rollback-for属性没有指定异常或者默认不写:经测试事务只回滚运行时异常(RuntimeException)和错误(Error). <!-- 配置事 ...

  9. spring 事务注解

    在spring中使用事务需要遵守一些规范和了解一些坑点,别想当然.列举一下一些注意点. 在需要事务管理的地方加@Transactional 注解.@Transactional 注解可以被应用于接口定义 ...

随机推荐

  1. linux服务之dns

    安装dig工具 [root@cu-app-107 ~]# cat /etc/redhat-releaseCentOS Linux release 7.5.1804 (Core) [root@cu-ap ...

  2. IDEA 修改某个Module名称

    一.选择module右键——>Refactor——>Rename 二.修改该module下的pom.xml文件对应module名改掉 三.修改项目的pom文件中modules里的modul ...

  3. JSOUP 爬虫

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.mavne 依赖: <!--html 解析 : jsoup HTML parser library @ ...

  4. 0.9.0.RELEASE版本的spring cloud alibaba nacos+gateway网关实例

    gateway就是用来替换zuul的,功能都差不多,我们看下它怎么来跟nacos一起玩.老套路,三板斧: 1.pom: <?xml version="1.0" encodin ...

  5. haproxy高可用、负载均衡集群

    HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.根据官方数据,其最高极限支持10G的并发.另外其支持从4层至7层的网络交换, ...

  6. 123457123456#1#----com.ppGame.BaoBaoiMiYu35--前拼后广--miyu_pp

    com.ppGame.BaoBaoiMiYu35--前拼后广--miyu_pp

  7. SharpGL学习笔记(一) 平台构建与Opengl的hello World (转)

    (一)平台构建与Opengl的hello World OpenGL就是3d绘图的API,微软针和它竞争推出D3D,也就是玩游戏时最常见的DirectorX组件中的3d功能. 所以不要指望windows ...

  8. Swift4.0复习函数

    1.函数的定义与调用: 一个函数定义的基本语法如以下代码所示: func function_name (param1: Int, param2: Float, param3: Double) -> ...

  9. 【端口转发】k8s port-forward端口转发 和 ssh -NfL端口转发

    kubectl port-forward端口转发 将远程pod端口转发到本地端口 kubectl port-forward monitoring-grafana-695c545f46-rhtwc -- ...

  10. easyui datagrid里的toobar按钮隐藏、显示、禁用等方式的实现

    easyui datagrid里的toobar按钮隐藏.显示.禁用等方式的实现 //隐藏第一个按钮 $('div.datagrid-toolbar a').eq(0).hide(); //隐藏第一条分 ...