How to continue transaction in Spring Boot with PostgreSQL after an exception is thrown?

I created a service method that creates user accounts. If creation fails because the given email is already in our database, I want to send the user an email with which they are already registered:

@Transactional(noRollbackFor=DuplicateEmailException.class)
void registerUser(User user) {
   try {
      userRepository.create(user);
   catch(DuplicateEmailException e) {
      User registeredUser = userRepository.findByEmail(user.getEmail());
      mailService.sendAlreadyRegisteredEmail(registeredUser);
   }
}

      

This does not work. Although I've marked it DuplicateEmailExcepetion

as "no rollback", the second SQL query (findByEmail) still fails because the transaction was aborted.

What am I doing wrong?

There is no annotation in the repository @Transactional

.

+3


source to share


4 answers


This is not a Spring / JDBC problem or your code, the problem is with the database. For example, when using Postgres, if any statement fails in a transaction, all subsequent statements will fail with current transaction is aborted

.

For example, do the following in Postgres:



> start a transaction
> DROP SEQUENCE BLA_BLA_BLA;
> Error while executing the query; ERROR: sequence "BLA_BLA_BLA" does not exist"
> SELECT * FROM USERS;
> ERROR: current transaction is aborted, commands ignored until end of transaction block

      

However, SELECT and subsequent statements are expected to be successful against MySQL, Oracle and SQL Server

+4


source


Why don't you change your logic like this:

void registerUser(User user) {
   User existingUser = userRepository.findByEmail(user.getEmail())
   if(existingUser == null){
         userRepository.create(user);
   }else{
       mailService.sendAlreadyRegisteredEmail(existingUser)
   }
}

      



This will ensure that only non-existent users are included in the database.

+1


source


You can wrap the call to userRepository in a try catch block. Or you can look ahead if the user exists and lean on creating a new one.

-1


source


@Transactional

the annotation is not positioned correctly. Spring creates an AOP advisor around the method where the annotation is defined @Transactional

. So in this case the pointcut will be created around the method registedUser

. But the method registerUser

doesn't throw DuplicateEmailException

. Therefore, rollback rules are not evaluated.

You need to define a rule @Transactional

around the method UserRepository.createUser

. This ensures that the transaction pointcut generated by Spring is not rolled back due to DuplicateEmailException

.

public class UserRepository {

    @Transactional(noRollbackFor=DuplicateEmailException.class)
    public User createUser(){
        //if user exist, throw DuplicateEmailException
    }
}

void registerUser(User user) {
    try {
       userRepository.create(user);
    catch(DuplicateEmailException e) {
       User registeredUser = userRepository.findByEmail(user.getEmail());
       mailService.sendAlreadyRegisteredEmail(registeredUser);
    }
}

      

-1


source







All Articles