在发生异常后,如何使用PostgreSQL在Spring Boot中继续执行事务?

时间:2017-05-09 08:07:18

标签: java sql postgresql spring-boot jdbctemplate

我创建了一个创建用户帐户的服务方法。如果由于给定的电子邮件地址已经在我们的数据库中而导致创建失败,我想向用户发送一封电子邮件,说明他们已经注册:

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

这不起作用。虽然我将DuplicateEmailExcepetion标记为"没有回滚",但第二个SQL查询(findByEmail)仍然失败,因为事务已中止。

我做错了什么?

存储库中没有@Transactional注释。

4 个答案:

答案 0 :(得分:6)

这不是Spring / JDBC或您的代码的问题,问题在于底层数据库。例如,当您使用Postgres时,如果任何语句在事务中失败,则所有后续语句都将失败并显示current transaction is aborted

例如,在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

预计SELECT和后续语句仍将成功对抗MySQL,Oracle和SQL Server

答案 1 :(得分:1)

为什么不改变逻辑如下:

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

这将确保只将非现有用户插入数据库。

答案 2 :(得分:-1)

您可以在try catch块中将调用包装到userRepository。或者,如果用户存在,您可以先查看,然后创建一个新用户。

答案 3 :(得分:-1)

@Transactional注释放置不正确。 Spring围绕定义@Transactional注释的方法创建了AOP顾问程序。因此,在这种情况下,将在registedUser方法周围创建切入点。但是,registerUser方法不会抛出DuplicateEmailException。因此,不会评估回滚规则。

您需要围绕@Transactional方法定义UserRepository.createUser规则。这将确保spring创建的Transaction切入点不会因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);
    }
}