Spring AOP事务处理
Spring 中事务简介
事务定义
事务(Transaction)是一个业务,是一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性。
事务特性
事务具备ACID特性,分别是:
-
原子性(Atomicity):一个事务中的多个操作要么都成功要么都失败。
-
一致性(Consistency): 例如存钱操作,存之前和存之后的总钱数应该是一致的。
-
隔离性(Isolation):事务与事务应该是相互隔离的。
-
持久性(Durability):事务一旦提交,数据要持久保存。
在实际开发中,为了做一定的优化,在一致性方面,可以只保持最终的一致就可以了,这样的事务通常我们会称之为柔性事务
Spring 中事务管理
Spring 中事务方式概述
Spring框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦。也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制。
在SpringBoot项目中,其内部提供了事务的自动配置,当我们在项目中添加了指定依赖spring-boot-starter-jdbc时,框架会自动为我们的项目注入事务管理器对象,最常用的为DataSourceTransactionManager对象。
Spring 中事务管理实现
在spring中,进行事务的控制与管理主要通过@Transactional注解进行声明式事务管理
实现步骤:
第一步:启用声明式事务管理,在项目启动类上添加@EnableTransactionManagement,新版本中也可不添加
第二步:将@Transactional注解添加到合适的业务类或方法上,并设置合适的属性信息。
/**
* 如果在类的上面使用了事务的注解,而方法上面没有使用事务的注解
* 那么方法上的事务默认会使用类上面注解事务的属性,也就是共性
* 但是一旦方法上使用了事务的注解,那么在该方法上的事务的注解的属性就
* 不会再使用类上面的注解的属性.而是使用其特性,也就是说一旦有特性,共性就会失效
* 如果特性没有明确,那么就是默认的
*/
@Transactional(timeout = 30,
readOnly = false,
isolation = Isolation.READ_COMMITTED,
rollbackFor = Throwable.class,
propagation = Propagation.REQUIRED)
@Service
public class implements SysUserService {
@Transactional(readOnly = true)
@Override
public PageObject<SysUserDept> findPageObjects(
String username, Integer pageCurrent) {
//......
}
}
注意:
- @Transactional注解应用在类上时表示类中所有方法启动事务管理,并且一般用于事务共性的定义。当
- @Transactional描述方法时表示此方法要进行事务管理,假如类和方法上都有@Transactional注解,则方法上的注解一般用于事务特性的定义。
@Transactional 常用属性应用说明:
- timeout:事务的超时时间,默认值为-1,表示没有超时显示。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务开启以后到sql语句执行之前。
- read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
- rollback-for:用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
- no-rollback- for 抛出 no-rollback-for 指定的异常类型,不回滚事务。
- isolation事务的隔离级别,默认值采用 DEFAULT。
Spring 中事务传播特性
事务传播(Propagation)特性指"不同业务(service)对象"中的事务方法之间相互调用时,事务的传播方式常用的有两种:
@Transactional(propagation=Propagation.REQUIRED) 。
@Transactional(propagation=Propagation.REQUIRES_NEW)。
如何理解不同业务对象呢?
举个简单的例子
当我们在加载一个用户列表页面的时候,执行的是一个用户业务,但是同时我们需要将用户的操作写入日志,这样的话我们就在一个用户的操作用又应用了一个日志的业务操作,这就是属于不同的业务对象.
@Transactional(propagation=Propagation.REQUIRED) 。该事务传播方式,是spring中默认的事务传播方式它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
示例代码:
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveObject(SysLog sysLog) {
sysLogDao.insertObject(sysLog);
}
//使用以上的事务传播的方式时,那么该方法始终工作在一个已经存在的事务方法中
//或者是由调用者创建的一个事务方法中.那么如果使用了自定义的注解以后,并且没有对
//自定义的注解设置优先级的话,那么这个注解的优先级就是比较低的,此时事务如果设置了只读操作
//那么再调用的方法中的事务存在了写操作的话,就会出现异常,一种解决方案就是我们可以对自定义
//的注解进行优先级设置,让其优先于事务的注解.或者采用事务传播特性的另一个特性:Propagation.REQUIRES_NEW,使用这个特性也可以解决
@Transactional(propagation=Propagation.REQUIRES_NEW)。必须是新事务, 如果有当前事务, 挂起当前事务并且开启新事务,也就是说使用这个注解后,不管外面有多少的限制,在执行这个业务的时候会单独开启一个新的事务,保证数据的一致性操作.
示例代码:
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveObject(SysLog sysLog) {
sysLogDao.insertObject(sysLog);
}
//当有一个业务对象调用如上业务方法时,此方法会始终运行在一个新的事务中。
例如以下方法调用这个方法,但是以下的方法进行了事务的只读操作控制,但是还可以保证上述方法的写操作完整执行.
@Transactional(readOnly = true)
@RequiredLog("用户查询")//该注解可以通过aop进行横切,调用saveObject方法.
@Override
public PageObject<SysUserDept> findPageObjects(String username, Integer pageCurrent, Integer pageSize) {
//参数校验
if (pageCurrent==null||pageCurrent<1) {
throw new IllegalArgumentException("页码值不正确");
}
}
Spring 中事务管理小结
Spring 声明式事务是 Spring 最核心,最常用的功能。由于 Spring 通过 IOC 和 AOP的功能非常透明地实现了声明式事务的功能,对于一般的开发者基本上无须了解 Spring声明式事务的内部细节,仅需要懂得如何配置就可以了。