一、spring 事务注解
1、实现层(方法上加)
import org.springframework.transaction.annotation.Transactional;
@Transactional(rollbackFor = Exception.class)
public JsonResult getRtransactional() {
// 手动标记事务回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
二、事务特性
1、@Transactional注解是用于事务控制的
需要知道事务的 ACID 特征:
- 原子性(Atomicity,或称不可分割性)
- 一致性(Consistency)
- 隔离性(Isolation,又称独立性)
- 持久性(Durability)
事务是用来控制数据的 ACID 特性的,用于保证数据的正确性和完整性。
2、@Transactional 注解有两种使用方式:
- 标注在类上面:当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
- 标注在方法上面:当作用于方法上时,只有当该方法发生了异常才会进行回滚,其他的方法不受影响。
在项目中使用,@Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。
三、事务传播行为与隔离级别
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
1、事物传播行为介绍:
2、事物超时设置:
@Transactional(timeout=30) //默认是30秒
3、事务隔离级别:
// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE):
// MYSQL: 默认为REPEATABLE_READ 可重复读 级别
// SQLSERVER: 默认为READ_COMMITTED 读取已提交数据
脏读 : 一个事务读取到另一事务未提交的更新数据。
不可重复读 : 在同一事务中,多次读取同一数据返回的结果有所不同,即后续读取可能读到另一事务已提交的更新数据, 相反,
可重复读:在同一事务中多次读取数据时能够保证所读数据一样,即后续读取不能读到另一事务已提交的更新数据。
幻读 : 一个事务读到另一个事务已提交的insert数据。
四、spring支持 编程式事务管理 和 声明式事务管理 两种方式
1、编程式事务管理使用 TransactionTemplate 或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring 推荐使用 TransactionTemplate。
2、声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务管理也有两种常用的方式:一种是基于tx和aop名字空间的xml配置文件;另一种是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
使用注解形式的方式使用事务需要在xml配置文件中配置如下信息
<!-- 服务事务管理 -->
<bean id="demoTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="demoDataSource"/>
</bean>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="demoTransactionManager"/>
五、需要注意的几点如下
1、@Transactional 注解只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能。
2、用 spring 事务管理器,由 spring 来负责数据库的打开、提交、回滚,默认遇到运行期异常(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的异常时执行回滚;而遇到需要捕获的异常(throw new Exception("注释");)不会回滚,即遇到受检查的异常(就是非运行时抛出的异常,编译器会检查到的异常叫受检异常)时,需要我们指定方式来让事务回滚。如果要想所有异常都回滚,则要加上@Transactional( rollbackFor=Exception.class),
如果让unchecked异常不回滚: @Transactional(notRollbackFor=RunTimeException.class)。如下代码所示:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}
3、@Transactional 注解应该只被应用到public可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不具有事务功能。
4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别。上面的例子中,其实正是元素的出现开启了事务行为。也就是说要有具体的 bean 出现时事务才会起作用。
5、Spring 团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别。因此,请接受 Spring 团队的建议,必须接受啊!
六、@Transactional注解所具有的属性
七、事务可能导致锁表
事务有可能导致锁表,从而导致查询操作超时。锁表的原因是多个事务同时操作某一张表。
mysql事务锁表参考博文:MySQL 死锁-CSDN博客
(mysql事务锁表导致查询超时)
mysql中主要是注意以下三张表:
select * from information_schema.INNODB_LOCKS;
select * from information_schema.INNODB_LOCK_WAITS;
select * from information_schema.INNODB_TRX;