环境:SpringBoot3.2.5
1. 简介
编程式事务允许开发者在代码中明确控制事务的边界、启动、提交和回滚等操作,从而确保数据的一致性和完整性。编程式事务允许开发者根据业务逻辑将多个数据库操作组合成一个事务,或者将一个大事务拆分成多个小事务。这样可以避免不必要的数据锁定,提高系统的并发性能。
Spring框架提供了两种编程事务管理方法:
- TransactionTemplate 或 TransactionalOperator。
- 直接实现TransactionManager。
Spring 官方推荐使用 TransactionTemplate 在命令式流程中进行编程式事务管理,并推荐使用 TransactionalOperator 在反应式代码中进行编程式事务管理。
接下来将对编程式事务的应用进行详细的介绍。先看如下代码,这段代码就可以通过编程式事务来进行优化:
@Transactional
public void saveXXX(...) {
Domain result = restTemplate.getForObject(url, Domain.class) ;
userDao.save(xxx) ;
}
这里http接口调用的操作不应该放到一个事务中操作它和数据库就没有关系,但是它的耗时越长就会对当前的数据库连接占有更长的时间对系统的整体性能是有影响的。
当然这里的操作也完全可以通过其它的方式解决。
2. 实战案例
2.1 环境准备
// 实体类
@Entity
@Table(name = "BC_USERS")
public class Users{
private String username ;
private String password ;
private Integer status = 0 ;
}
@Mapper
public interface UsersMapper {
int insertUser(Users user) ;
}
// UsersMapper.xml
<insert id="insertUser" parameterType="com.pack.domain.Users">
insert into bc_users (id, username, password) values (#{id}, #{username}, #{password})
</insert>
接下来分别通过不同的方式对编程式事务进行详解的讲解。
2.2 TransactionTemplate
TransactionTemplate采用与其他Spring模板(如JdbcTemplate)相同的方法。它使用回调方法。通过调用TransactionTemplate#execute(…)方法,该方法接收一个TransactionCallback参数,该接口是一个函数式接口,我们只需将需要在事务上下文中执行的代码放入方法中即可。接口如下:
@FunctionalInterface
public interface TransactionCallback<T> {
@Nullable
T doInTransaction(TransactionStatus status);
}
将我们的业务代码放入doInTransaction方法中即可。该接口是有返回值的,它还有一个抽象子类TransactionCallbackWithoutResult,如果你没有返回值则可以使用该类。
public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
@Override
@Nullable
public final Object doInTransaction(TransactionStatus status) {
doInTransactionWithoutResult(status);
return null;
}
protected abstract void doInTransactionWithoutResult(TransactionStatus status) ;
}
如果没有返回值可以子类化该类。
- 有返回值
@Service
public class UserService {
@Resource
private TransactionTemplate transactionTemplate ;
public Integer saveUsers(Users users) {
Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return usersMapper.insertUser(users) ;
}
}) ;
return result ;
}
}
实际需要事物操作的只需要写到doInTransaction方法中即可。
- 无返回值
通过子类化TransactionCallbackWithoutResult
public void saveUsers(Users users) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
usersMapper.insertUser(users) ;
}
}) ;
}
基本与有返回值情况使用方式一致。
- 事务回滚
事务的回滚通过TransactionStatus#setRollbackOnly方法。
public Users saveUser(Users users) {
return transactionTemplate.execute(new TransactionCallback<Users>() {
@Override
public Users doInTransaction(TransactionStatus status) {
try {
return usersMapper.insertUser(users) ;
} catch (Exception e) {
// 回滚
status.setRollbackOnly() ;
}
return null ;
}
}) ;
}
这里根据实际业务情况可通过不同的异常信息进行事务的回滚操作。
- 事务属性配置
以上我们是通过直接注入系统默认创建的TransactionTemplate进行使用,都使用的是默认行为,我们可以通过如下的方式进行自定义属性的配置:
private TransactionTemplate transactionTemplate ;
public UserService(PlatformTransactionManager transactionManager) {
// 自己构建事务模版
this.transactionTemplate = new TransactionTemplate(transactionManager) ;
// 事务传播行为
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) ;
// 设置超时时间
this.transactionTemplate.setTimeout(30); // seconds
}
如上配置后,我们通过如下代码进行测试是否生效
public Integer updateUsers(Integer statusValue, String id) {
return transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
// 将这里的操作放到一个事务中,但是上面我们配置的是不支持事务,所以应该报错
return usersRepository.updateUsers(statusValue, id) ;
}
}) ;
}
// Repository操作,更新状态
@Modifying
@Query("update Users u set u.status=?1 where u.id=?2")
int updateUsers(Integer status, String id) ;
运行结果
2.3 TransactionalOperator
该类是基于响应式编程中应用的编程式事务,这里就不做介绍了。
2.4 TransactionManager
该接口的有2个子接口分别是:
- PlatformTransactionManager
- ReactiveTransactionManager(基于响应式)
这里不对响应式对介绍。
如下示例通过PlatformTransactionManager来控制事务的提交与回滚。
private PlatformTransactionManager transactionManager ;
private DefaultTransactionDefinition definition ;
private TransactionStatus status ;
@Resource
private UsersRepository usersRepository ;
public UserService3(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager ;
// 事务定义,比如:事务的传播特性
definition = new DefaultTransactionDefinition() ;
definition.setName("pgName") ;
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED) ;
}
public Integer saveUsers(Users users) {
TransactionStatus status = this.transactionManager.getTransaction(definition) ;
Integer result = null ;
try {
result = usersMapper.insertUser(users) ;
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(status) ;
throw e ;
}
// 提交事务
transactionManager.commit(status) ;
return result ;
}
以上代码的流程还是非常清晰的。