Springboot編程式事務使用方式詳解


環境:springboot2.3.9.RELEASE


Spring提供兩種編程式事務管理方法:

  1. 使用TransactionTemplate 或 TransactionalOperator
  2. 直接創建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實戰案例錦集

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM