spring-bootjpatransactionsrollbackpropagation

JPA @Transactioanl REQUIRES_NEW AND Catch RuntimeException But rollback


@Service
@RequiredArgsConstructor
@Transactional
public class UserService {

  public void createUser(CreateUserRequest request) {
    Users users = firebaseUsersRepository.findUsersByFirebaseUid(request.getFirebaseUid())
        .orElseThrow(() -> new BusinessException("Not Found User", HttpStatus.INTERNAL_SERVER_ERROR));
    User user = User.builder()
        .name(users.getDisplay_name())
        .firebaseUid(request.getFirebaseUid())
        .build();
    userRepository.save(user);
  }

}



@Component
@RequiredArgsConstructor
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class BaseEntityAuditAware implements AuditorAware<User> {

  private final UserRepository userRepository;

  @Override
  public Optional<User> getCurrentAuditor() {
    try {
      return userRepository.findById(ApiLogger.getRequestCallerId());
    } catch (Exception e) {
      return Optional.empty();
    }
  }
}

When calling userRepository.save(user) in createUser, After retrieving user information from BaseEntityAuditAware, which was implemented to use the JpaAudit function, Send an actual query.

At this time, the propagation property is REQUIRE_NEW, and all exceptions that occurred were caught. I expected that there would not be a rollback because the exception would not be passed to the parent function that called this function. In fact, an unexpectedrollbackexception occurred and the rollback occurred.

Returning null is not a problem,

In order to actually avoid raising an exception, I changed the above code as follows, and the insert query was successfully executed with a null value.

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
  public Optional<User> getCurrentAuditor() {
    Long callerId = ApiLogger.getRequestCallerId();
    if (callerId == null)
      return Optional.empty();
    return userRepository.findById(ApiLogger.getRequestCallerId());
  }

why rollback??

changed Transactional Option to @Transactional(propagation = Propagation.REQUIRES_NEW, noRollBackFor = Exception.class) but same.


Solution

  • i found.

    [Call Stack]
    [Transaction A] createUser()  
    [Transaction B] ㄴ getCurrentAuditor() 
    [Transaction B] ㄴ userRepository.findById(ApiLogger.getRequestCallerId()) 
    ==> throw RuntimeException
    

    So Transaction B has ROLL-BACK Marking

    And when getCurrentAuditor() try to commit, Because Of Roll-back marking, Transaction AOP Throws UnexpectedRollbackException.

    Because this Excxception is from Transaction AOP, B cannot catch this Exception.

    Finally, Transaction A rollback because it thinks an RuntimeException(=UnexpectedRollbackExCeption) occurred.