Problem with transaction annotations not taking effect

There are two main reasons for this problem:

  • The Spring transaction did not take effect
  • Cannot roll back normally when an exception occurs

Spring transaction does not take effect

  1. Transactional is used on non-public methods
  2. Transaction methods are not invoked through proxy means

Transaction annotations are used on non-public methods

@Transactional

private Access createRecords(AccessDto accessDto);

Not invoked by proxy: The Service is normally injected into Spring, and the transaction method is called using This when the method is called. Spring does not inject This, so transactions cannot be used. You can use Controller to call Service directly when calling yourself

//AccessServiceImpl.class

//this.AccessRecords(AccessDto accessDto);

A condition that cannot be rolled back when an exception occurs

  1. If the exception is not handled correctly, the transaction may not be rolled back
  2. Multiple database operations, no transaction propagation configured

When a transaction takes effect and cannot be rolled back:

The try/catch wrapper marks methods with the @Transactional annotation. Methods are rolled back only if they meet certain criteria.

Certain conditions:

The transaction is rolled back only if the exception propagates a method other than the @Transactional annotation marked.

By default, Spring rolls back transactions only when a RunTimeException or Error occurs. The function of catch is to catch the exception in the method, so that the rollback does not propagate to the outer transaction, so as not to have an impact on other transactions.

public int createUserWrong1(String name) {
 try {
     this.createUserPrivate(new UserEntity(name));
 } catch (Exception ex) {
     log.error("create user failed because {}", ex.getMessage());
 }
 return userRepository.findByName(name).size();
}
Copy the code

RunTimeException is a subclass of Exception, which wraps all exceptions so that they cannot be propagated and rolled back.

The solution of ①

Option 1: Manually set rollback:

// Catch an exception:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Copy the code

Use the @Transactional attribute

// Roll back any exception thrown
@Transactional(rollbackFor = Exception.class)
Copy the code

The above formula is clearly not applicable in this case:

During user registration, new data is added to the primary table, and the primary table data needs to be associated with the sub-table. Now, services require that the sub-table is rolled back when the sub-table fails to execute. The sub-table does not affect the transactions of the primary table, that is, the sub-table cannot affect the main process, and the primary table and sub-table must not be in the same transaction.

Solution:

@Transactional(propagation = Propagation.REQUIRES_NEW)

A new transaction is created when the current method is executed.

Official documents:

/** * Create a new transaction, and suspend the current transaction if one exists. * Analogous to the EJB transaction attribute of the same name. * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box * on all transaction managers. This in particular applies to * {@link org.springframework.transaction.jta.JtaTransactionManager}, * which requires the {@code javax.transaction.TransactionManager} to be * made available to it (which is server-specific  in standard Java EE). * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager */Copy the code