【问题标题】:Unique Constraint error thrown at the end of @Transactional block@Transactional 块末尾抛出的唯一约束错误
【发布时间】:2020-12-02 09:57:38
【问题描述】:

假设我有一个用户类:

@Entity
@Data
@Builder
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long userKey;

    @Column(unique = true)
    String someId;

    String name;
}

以及对应的服务

@Component
@Slf4j
public class UserService {

    @Autowired
    UserRepository repository;

    @Transactional
    public User createUserWithId(String name, String id) {
        User userToAdd = User.builder()
                .name(name)
                .someId(id)
                .build();
        repository.save(userToAdd);
        log.info("No issue in saving");
        //some more code
        return userToAdd;
    }

}

如您所见,我对 User 类中的 someId 字段有一个唯一约束,但是当我使用数据库中已经存在的 someId 中的值执行 createUserWithId 方法时,我希望在包含的行上出现错误repository.save() 和它之后的代码不会被执行。但是它被执行之后的代码,我在事务块的末尾遇到了一个异常。我的问题是为什么会发生这种情况,在与存储库对象交互时我通常会遇到哪些异常(例如在本例中是 repository.save )以及在事务块结束时我会遇到哪种类型的异常?

PS 我从一个简单的控制器内部调用 UserService,并创建了一个空的 UserRepository,它只是扩展了 CrudRepository。为简洁起见,我将这两个问题都省略了,但如果在此处添加它们是否有意义,请告诉我。

编辑 1:根据 cmets 中的请求添加用户存储库

@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}

【问题讨论】:

  • 分享UserRepository代码。
  • 当会话状态(来自您的 ORM 映射器)与数据库同步时会引发异常。通常这是在提交事务时。如果你想强制它,你需要在 JPA EntityManager 上调用`flush`(或扩展JpaRepository 而不是CrudRepository 并调用saveAndFlush)。
  • 谢谢@M.Deinum 当我在事务块中调用保存/删除/插入时,我应该期望所有类型的约束都被忽略,还是这些约束的特定子集只在事务块结束?

标签: java spring hibernate jpa transactions


【解决方案1】:

错误发生在拦截器中,因为在提交事务之前,Hibernate 需要刷新数据库的未决更改。在刷新期间,会发生数据库异常。您可以通过在存储库上调用 saveAndFlush 来手动刷新。

【讨论】:

    【解决方案2】:

    重复时发生唯一错误。 spring 有数据库错误帮助类,你可以在控制器层上捕获数据库异常,由@transactional 传递给控制器​​。

      } catch (DataAccessException ex) {
    

     } catch (DataIntegrityViolationException ex) {
    

    如果数据库连接器具有标准异常抛出支持。

    在你的情况下,我认为你错过了

    @Transactional(readOnly = false)
    

    【讨论】:

      【解决方案3】:

      当我们打电话时

      repository.save(obj); 
      

      hibernate将此实体保存在内存中,该实体会在Transaction中的方法处持久化。

      还有一种方法来做这种操作,首先你应该尝试在 id 上获取结果

      repository.findById(id)
      

      并检查是否为空,并进行保存操作。

      【讨论】:

        猜你喜欢
        • 2021-04-16
        • 2018-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多