数据库事务失效

数据库事务(Database Transaction):将有限系列的执行命令作为单个逻辑执行单元,单元内的任务要么全部 成功,要么全部失败。数据库事务拥有四大特性,通常称为ACID,具体说明如下

  1. 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  2. 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
  3. 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  4. 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中

spring的注解@Transactional使用也存在一些限制和注意的问题还是来看看具体的例子,看看到底是什么原因导致事务失效呢?

1. 无事务

方法不加注解抛异常,执行有异常抛出,z1表成功新增一条记录

1
2
3
4
5
6
7
8
public void testTransaction() {
db.update("insert into z1(c1) values('2')");
// 主动抛出异常 测试回滚
String str = null;
if (str.startsWith("111")) { // 空指针异常
db.update("insert into z2(c1,c2) values('2','3')");
}
}
2.有事务且是同一个方法

方法加注解抛异常,执行有异常抛出,z1、z2表都没有新增记录,事务正常

1
2
3
4
5
6
7
8
9
@Transactional
public void testTransaction() {
db.update("insert into z1(c1) values('2')");
// 主动抛出异常 测试回滚
String str = null;
if (str.startsWith("111")) { // 空指针异常
db.update("insert into z2(c1,c2) values('2','3')");
}
}
3.有事务且事务仅在第一个方法上

方法加注解并调用该类其他方法并抛异常,执行有异常抛出,z1、z2表无新增记录

1
2
3
4
5
6
7
8
9
10
11
12
@Transactional
public void testTransaction() {
db.update("insert into z1(c1) values('2')");
testTransaction2();
}
public void testTransaction2() {
// 主动抛出异常 测试回滚
String str = null;
if (str.startsWith("111")) {
db.update("insert into z2(c1,c2) values('2','3')");
}
}
4.有事务事务在被调用者上,本地方法调用

调用含注解的方法并抛异常,执行有异常抛出,z1表新增记录、z2表无新增记录,事务失效

1
2
3
4
5
6
7
8
9
10
11
12
public void testTransaction() {
testTransaction3();
}
@Transactional
public void testTransaction3() {
db.update("insert into z1(c1) values('2')");
// 测试
String str = null;
if (str.startsWith("111")) {
db.update("insert into z1(c1) values('adff2')");
}
}

为什么事务会失效呢?关于这一点Spring Transactional官方说明如下:

  1. 在代理下(默认或当配置为proxy-target-class=”true”),只有当前代理类的外部方法调用注解方法时代理才会被拦截。事实上,这意味着:一个目标对象的方法调用该目标对象的另外一个方法,即使被调用的方法已使用了@Transactional注解标记,事务也不会有效执行。
  2. AOP使用的是动态代理的机制,它会给类生成一个代理类,注解相关操作都在代理类上完成。内部方式使用this或者直接调用时,使用的是实例调用,并没有通过代理类调用方法,所以会导致注解失效。
  3. @Transactional注解只对代理类时的public方法有效,被protected、private、package-visible修饰的方法使用@Transactional注解无效