Java-Spring事务
事务
- 什么是事务?
一系列操作要么全部完成,要么全部失败.
-
事务的特新(ACID)?
1. 原子性:事务最小的执行单位,不可分割的,要么全部完成,要么都不起作用. 2. 一致性:事务执行前后,数据保持一致. 3. 隔离性:并发时,一个事务不能影响另一个事务的执行. 4. 持久性:事务一旦提交就是永久的,哪怕数据库故障也不会有影响.
Spring中的事务
程序是否支持事务取决于数据库,以Mysql为例,如果是myisam引擎的话,是不支持事务的.在Spring中事务有2种,编程式事务,声明式事务.
- 编程式事务.
使用TransactionTemplate编写事务代码,当捕捉到异常时,执行回滚操作.
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
- 声明式事务.
推荐使用声明式事务,代码侵入低. @Transactional注解,基于AOP实现.
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
C c = new C();
b.bMethod();
c.cMethod();
}
- 事务传播行为?
当多个事务存在时,如何处理事务.
1. TransactionDefinition.PROPAGATION_REQUIRED
默认值,当前存在事务就加入该事务,不存在事务就新建事务.
注意:aMethod和bMethod只要有一个回滚,整个事务均回滚.即外部事务被PROPAGATION_REQUIRED修饰时,内部所有也被PROPAGATION_REQUIRED修饰的事务都是一个事务.
//代码
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
}
}
Class B {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void bMethod {
//do something
}
}
2. TransactionDefinition.PROPAGATION_REQUIRES_NEW
无论当前是否存在事务,被其修饰的方法都会开启新的事务,且事务互相独立.
注意:如下,使其修饰bMethod,aMethod不变;若aMethod异常回滚,bMethod不受影响.但是bMethod回滚,aMethod也会回滚.
//代码
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
}
}
Class B {
@Transactional(propagation=propagation.REQUIRES_NEW)
public void bMethod {
//do something
}
}
3. TransactionDefinition.PROPAGATION_NESTED
如果外部没有事务,那么内部事务新建事务且互相独立互不影响.
如果外部有事务,那么内部事务属于外部事务的子事务,外部事务回滚,子事务回滚;子事务回滚不会影响外部事务.
注意:如果 aMethod() 回滚的话,bMethod()和bMethod2()都要回滚,而bMethod()回滚的话,并不会造成 aMethod() 和bMethod()回滚。
//代码
Class A {
@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
//do something
B b = new B();
b.bMethod();
b.bMethod2();
}
}
Class B {
@Transactional(propagation=propagation.PROPAGATION_NESTED)
public void bMethod {
//do something
}
@Transactional(propagation=propagation.PROPAGATION_NESTED)
public void bMethod2 {
//do something
}
}
4. TransactionDefinition.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 基本不用.
-
以下配置发生错误也不会回滚
1. TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 2. TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 3. TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
-
事务超时属性?
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1。
-
事务只读属性?
对于只有查询操作的事务,可以设置为readonly,即只读事务,一般用于多条数据库查询.
-
为什么只读还要加事务?
以MySQL的innodb为例: MySQL 默认对每一个新建立的连接都启用了autocommit模式。在该模式下,每一个发送到 MySQL 服务器的sql语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务,并开启一个新的事务。
-
加Transactional:
所有的查询sql会放到一个事务中.
-
不加Transactional:
每条sql会开启一个单独的事务,中间被其它事务改了数据,都会实时读取到最新值。
- 什么时候使用只读事务?
-
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性.
-
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在两条SQL查询之间,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态.
- 事务回滚规则?
默认回滚 RuntimeException
的子类,也可以自己指定
@Transactional(rollbackFor= MyException.class)
- @Transactional 注解原理
如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候
为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,
TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。
- @Transactional 注解失效?
- @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
- 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
- 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败