环境:springboot2.3.9.RELEASE
Spring提供两种编程式事务管理方法:
- 使用TransactionTemplate 或 TransactionalOperator
- 直接创建TransactionManager的实现
Spring官方推荐使用TransactionTemplate方式
准备
// 实体类
@Entity
@Table(name = "BC_USERS")
@Data
public class Users{
private String username ;
private String password ;
private Integer status = 0 ;
}
// DAO
public interface UsersRepository extends JpaRepository<Users, String> {
@Modifying
@Query("update Users u set u.status=?1,u.password='123123' where u.id=?2")
int updateUsers(Integer status, String id) ;
}
@Mapper
public interface UsersMapper {
int insertUser(Users user) ;
}
// Mapper.xml
<insert id="insertUser" parameterType="com.pack.domain.Users">
insert into bc_users (id, username, password) values (#{id}, #{username}, #{password})
</insert>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
1 TransactionTemplate
1.1 有返回值的
@Service
public class UserService {
@Resource
private TransactionTemplate transactionTemplate ;
@Resource
private UsersRepository usersRepository ;
public Integer saveUsers(Users users) {
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return usersMapper.insertUser(users) ;
}
}) ;
return result ;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
1.2 无返回值的
当没有返回值时可以使用
TransactionCallbackWithoutResult
public void saveUsers(Users users) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
usersMapper.insertUser(users) ;
}
}) ;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
1.3 事务回滚
事务的回滚通过
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 ;
}
}) ;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
1.4 配置事务属性
在实例化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
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
测试代码
public Integer updateUsers(Integer statusValue, String id) {
return transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return usersRepository.updateUsers(statusValue, id) ;
}
}) ;
}
@Modifying
@Query("update Users u set u.status=?1 where u.id=?2")
int updateUsers(Integer status, String id) ;
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
由于这里事务传播属性设置的NOT_SUPPORTED.所以程序会报错误
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:403)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:531)
- 1.
- 2.
- 3.
- 4.
2 TransactionalOperator
TransactionalOperator适用于反应式编程,这里不做介绍。
3 TransactionManager
使用TransactionManager管理事务也有两种
PlatformTransactionManager,
ReactiveTransactionManager
ReactiveTransactionManager适用于反应式编程,这里不做介绍。
3.1 PlatformTransactionManager
在程序中可以使用
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) ;
publisher.publishEvent(new UsersEvent(users));
return result ;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
4 事务事件监听
通过@
TransactionalEventListener注解监听事务的不同阶段的事件信息
public @interface TransactionalEventListener {
TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
boolean fallbackExecution() default false;
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] value() default {};
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] classes() default {};
String condition() default "";
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
fallbackExecution: 默认值false;如果设置为true,当前即便没有事务也会触发事件。
TransactionPhase:默认值是事务提交以后;有如下几个取值:
public enum TransactionPhase {
BEFORE_COMMIT, // 事务提交前触发
AFTER_COMMIT, // 事务提交后触发
AFTER_ROLLBACK, // 事务回滚触发
AFTER_COMPLETION // 事务完成后 触发
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
注意:@
TransactionalEventListener注解只对声明式事务起作用,对编程式事务无效。仅适用于由PlatformTransactionManager管理的线程绑定事务
示例:
// 事件监听
@Component
public class TxListenerComponent {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUsersAfterCommit(UsersEvent usersEvent) {
Users user = (Users) usersEvent.getSource() ;
System.out.println("AfterCommit收到事件通知:" + user.getPassword()) ;
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void handleUsersAfterCompletion(UsersEvent usersEvent) {
Users user = (Users) usersEvent.getSource() ;
System.out.println("AfterCompletion收到事件通知:" + user.getPassword()) ;
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleUsersAfterRollback(UsersEvent usersEvent) {
Users user = (Users) usersEvent.getSource() ;
System.out.println("AfterRollback收到事件通知:" + user.getPassword()) ;
}
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleUsersBeforeCommit(UsersEvent usersEvent) {
Users user = (Users) usersEvent.getSource() ;
System.out.println("BeforeCommit收到事件通知:" + user.getPassword()) ;
}
}
// 发布事件
@Resource
private ApplicationEventPublisher publisher ;
@Resource
private UsersMapper usersMapper ;
public Integer saveUsers(Users users) {
Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return usersMapper.insertUser(users) ;
}
}) ;
publisher.publishEvent(new UsersEvent(users));
return result ;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
运行结果:
2021-06-17 14:02:56.830 DEBUG 10000 --- [nio-8081-exec-1] com.pack.mapper.UsersMapper.insertUser : ==> Preparing: insert into bc_users (id, username, password) values (?, ?, ?)
2021-06-17 14:02:56.840 DEBUG 10000 --- [nio-8081-exec-1] com.pack.mapper.UsersMapper.insertUser : ==> Parameters: mmmmm(String), mmmmm(String), mmmmm(String)
2021-06-17 14:02:56.842 DEBUG 10000 --- [nio-8081-exec-1] com.pack.mapper.UsersMapper.insertUser : <== Updates: 1
BeforeCommit收到事件通知:mmmmm
AfterCommit收到事件通知:mmmmm
AfterCompletion收到事件通知:mmmmm
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
总结:编程式的事务适合少量的事务操作;比如在一个服务的调用中有大量的计算操作,最后将计算结果进行事务的操作这种情况就适合应用事务编程式的进行事务控制。如果一个操作有很多的事务的操作那声明式的事务方式就更加的合适。