Spring支持两种事务方式:

  • 编程式事务:使用的是TransactionTemplate(或者org.springframework.transaction.PlatformTransactionManager接口)
  • 声明式事务:使用Transactional注解或者xml配置,建立在AOP之上的。本质就是对方法前后进行拦截,然后在目标方法开始之前创建或者添加一个事物,在执行完目标方法之后根据执行情况提交或者回滚事物

使用@Transactional注解时需要记住两个点:

        第一点:@Transactional 只能应用到 public 方法才有效

只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取@Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法

protected TransactionAttribute computeTransactionAttribute(Method method,Class<?> targetClass) {
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}

      第二点:避免 Spring 的 AOP 的自调用问题

      在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚

原因:调用的是原生方法,并不是代理对象的方法

   @Service
public class DemoService {
private void method1() {
insertOrder();
}
//当method2()被method1调用时,@Transactional失效
@Transactional
public void method2() {
//so some thing
}}

当然上面两个问题也是能解决的,使用 AspectJ 取代 Spring AOP 代理

1.认识@Transactional 注解的属性

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default ""; //当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 @AliasFor("value")
String transactionManager() default ""; //当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 Propagation propagation() default Propagation.REQUIRED; //事务的传播行为 Isolation isolation() default Isolation.DEFAULT; //事务的隔离级别 int timeout() default -1; //事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务 boolean readOnly() default false; //指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true Class<? extends Throwable>[] rollbackFor() default {}; //用于指定能够触发事务回滚的异常类型,可以指定多个异常类型 String[] rollbackForClassName() default {}; //类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组,和rollbackFor对应 Class<? extends Throwable>[] noRollbackFor() default {}; //抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 String[] noRollbackForClassName() default {}; //类名数组,必须继承自Throwable 导致事务不回滚的异常类名字数组,和noRollbackFor对应
}

2.propagation 属性,isolation 属性

事务的传播行为(TransactionDefinition定义):

Propagation.REQUIRED                    如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

Propagation.REQUIRES_NEW         重新创建一个新的事务,如果当前存在事务,暂停当前的事务。

Propagation.SUPPORTS                   如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

//下面使用场景较少

Propagation.MANDATORY                如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

Propagation.NOT_SUPPORTED       以非事务的方式运行,如果当前存在事务,暂停当前的事务。

Propagation.NEVER                          以非事务的方式运行,如果当前存在事务,则抛出异常。

Propagation.NESTED                        如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED

事务的隔离级别(TransactionDefinition定义):

ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是ISOLATION_READ_COMMITTED。

ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。

ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。

ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。

ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

Spring定义的TransactionDefinition接口:

 3.场景Demo测试

    实体类Company

@Data
@Builder
public class Company {
private String companyId;//公司编码
private String companyName;//公司名称
private String memo;//备注
}

测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class MytransactionApplicationTests {
@Autowired
private OneCompanyServiceImpl oneCompanyservice;
@Autowired
private TwoCompanyServiceImpl twoCompanyservice;
@Test
//测试Propagation.REQUIRED
public void test1() {
Company company = Company.builder().companyId("123").companyName("阿里巴巴").memo("杭州").build();
oneCompanyservice.multiInsert1(company);
}
@Test
//测试Propagation.REQUIRES_NEW
public void test2() {
Company company = Company.builder().companyId("123").companyName("腾讯").memo("深圳").build();
oneCompanyservice.multiInsert2(company);
}
}

    当前 oneCompanyService调用另一个employeeService

     1.测试 Propagation.REQUIRED

@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class)
public void multiInsert1(Company company) {
     oneComPanyDao.insert(company);
     Employee employee = Employee.builder().employeeId("123").employeeName("马云").memo("我对钱不感兴趣").build();
     employeeService.insert(employee);//@Transactional的Propagation为默认REQUIRED,加入当前事务
}

@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void insert(Employee employee) {
    employeeDao.insert(employee);
    int flag = 1/0;
}

结果:company和employee都没有插入数据库

   

 2.测试 Propagation.REQUIRES_NEW


@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class)
public void multiInsert2(Company company) {
       oneComPanyDao.insert(company);
       Employee employee = Employee.builder().employeeId("456").employeeName("马化腾").memo("我只是住的房子大一点").build();
       employeeService.insertOther(employee);// @Transactional的Propagation设置为REQUIRES_NEW,另启事务
       int flag = 1/0;//这里发生异常insertOther事务已经提交,不会回滚,insert方法回滚
}

@Override
@Transactional(value="mybatisTransactionManagerOne",rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void insertOther(Employee employee) {
    employeeDao.insert(employee);
}

结果:company没有插入数据库,employee插入数据库

      还有其他种Propagation行为,平时使用场景也比较少,不赘述

@Transactional之Spring事务深入理解的更多相关文章

  1. spring事务概念理解

    1.数据并发问题 脏读 A事务读取B事务尚未提交的更新数据,并在此数据的基础上操作.如果B事务回滚,则A事务读取的数据就是错误的.即读取了脏数据或者错误数据. 不可重复组 A事务先后读取了B事务提交[ ...

  2. spring事务的理解

    特性 一致性:业务处理要么都成功,要么都失败,不能部分成功不分失败 原子性:业务操作是由多个动作完成,这些动作不可分割,要么都执行,要么都不执行 隔离性:事务间之间要做隔离,不要互相影响 持久性:操作 ...

  3. myBatis 切换数据源(spring事务)理解

    1. mybatis (SqlSessionTemplate)的动态代理 a) sqlSession的结构 b)SqlSession 结构 public class SqlSessionTemplat ...

  4. spring事务再次理解

    2.2.3 只读 事务的第三个特性是它是否为只读事务.如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化.通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认 ...

  5. spring事务详细理解

    数据并发的问题 一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库.数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性.这些 ...

  6. Spring学习笔记(四)-- Spring事务全面分析

    通过本系列的文章对Spring的介绍,我们对Spring的使用和两个核心功能IOC.AOP已经有了初步的了解,结合我个人工作的情况,因为项目是金融系 统.那对事务的控制是不可缺少的.而且是很严格的控制 ...

  7. 就这?一篇文章让你读懂 Spring 事务

    什么是事务 ▲ 百度百科 概括来讲,事务是一个由有限操作集合组成的逻辑单元.事务操作包含两个目的,数据一致以及操作隔离.数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效:事务回滚 ...

  8. 深入理解 Spring 事务原理

    本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供 ...

  9. 深入理解 Spring 事务原理【转】

    本文转自码农网 – 吴极心原创  连接地址:http://www.codeceo.com/article/spring-transactions.html 一.事务的基本原理 Spring事务的本质其 ...

随机推荐

  1. leetcode 6 ZigZag Conversion(水题)

    就是简单的模拟一下就可以了.但是我一开始是用一个二维char数组来存的,这样在最终扫全体时会扫很多空的位置,浪费了很多时间,所以就time limit error了. 所以改进一下就用string数组 ...

  2. Android之ExpandableList扩展用法(基于BaseExpandableListAdapter)

    1.简介 基于基于BaseExpandableListAdapter扩展的ExpandableList用法,现在网上流行的主要有两种:第一种是向BaseExpandableListAdapter传入两 ...

  3. ACM学习历程—HDU 3949 XOR(xor高斯消元)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3949 题目大意是给n个数,然后随便取几个数求xor和,求第k小的.(重复不计算) 首先想把所有xor的 ...

  4. bzoj 4822~4824 CQOI2017题解

    老C的任务 题目大意: 维护一个二维平面,初始给出一些点及其权.多次询问某个矩形内的权和. n,m <= 100000 题解: 签到题. CDQ水一水. #include <cstdio& ...

  5. 51nod 1149 Pi的递推式 组合数

    题目大意: \(F(x) = 1 (0 \leq x < 4)\) \(F(x) = F(x-1) + F(x-\pi) (4 \leq x)\) 给定\(n\),求\(F(n)\) 题解: 我 ...

  6. 白话算法(6) 散列表(Hash Table)从理论到实用(上)

    处理实际问题的一般数学方法是,首先提炼出问题的本质元素,然后把它看作一个比现实无限宽广的可能性系统,这个系统中的实质关系可以通过一般化的推理来论证理解,并可归纳成一般公式,而这个一般公式适用于任何特殊 ...

  7. CF 360 E Levko and Game —— 贪心+最短路

    题目:http://codeforces.com/contest/360/problem/E 首先,每条边不是选 \( l[i] \) 就是选 \( r[i] \): 做法就是先把边权都设成 \( r ...

  8. 2006浙大火星A+B

    题目描述:     读入两个不超过25位的火星正整数A和B,计算A+B.需要注意的是:在火星上,整数不是单一进制的,第n位的进制就是第n个素数.例如:地球上的10进制数2,在火星上记为“1,0”,因为 ...

  9. C++STL 库中set容器应用

    #include<iostream> #include<cstdio> #include<set> using namespace std; set<int& ...

  10. 【转】 Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    目录(?)[-] xml控件代码重用include xml控件代码重用merge 横屏和竖屏landsacpe portrait xml控件代码重用:include 如果我们定义一个控件,需要在不同的 ...