環境: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 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 無返回值的
當沒有返回值時可以使用
TransactionCallbackWithoutResult
public void saveUsers(Users users) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { usersMapper.insertUser(users) ; } }) ; }
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.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 }
測試代碼
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) ;
由於這里事務傳播屬性設置的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)
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 ; }
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 ""; }
fallbackExecution: 默認值false;如果設置為true,當前即便沒有事務也會觸發事件。
TransactionPhase:默認值是事務提交以后;有如下幾個取值:
public enum TransactionPhase { BEFORE_COMMIT, // 事務提交前觸發 AFTER_COMMIT, // 事務提交后觸發 AFTER_ROLLBACK, // 事務回滾觸發 AFTER_COMPLETION // 事務完成后 觸發 }
注意:@
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 ; }
運行結果:
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
總結:編程式的事務適合少量的事務操作;比如在一個服務的調用中有大量的計算操作,最后將計算結果進行事務的操作這種情況就適合應用事務編程式的進行事務控制。如果一個操作有很多的事務的操作那聲明式的事務方式就更加的合適。
完畢!!!
給個關注+轉發吧謝謝
公眾:Springboot實戰案例錦集




