如何在使用 Hibernate 保存数据之前检查特殊条件

How to check special conditions before saving data with Hibernate

示例场景

我有一个控制列总值的限制。如果我进行了超过此限制的保存,我希望它抛出异常。例如;

假设我已经添加了以下数据:LIMIT = 20

id code value
1 A 15
2 A 5
3 B 12
4 B 3

我能做什么

我可以用所需的查询来检查这个场景。比如我给它写一个方法,我可以在save方法中查看。就是这样。

但是,我正在寻找比这更有用的解决方案

我可以举什么例子?

Hibernate 和 SpringData JPA 都没有针对这种情况的任何内置功能。您必须自己在存储库中编写事务逻辑:

@PersistenceContext
EntityManager em;

public addValue(String code, int value) {
    var checkQuery = em.createQuery("SELECT SUM(value) FROM Entity WHERE code = :code", Integer.class);
    checkQuery.setParameter("code", code);

    if (checkQuery.getSingleResult() + value > 20) {
        throw new LimitExceededException("attempted to exceed limit for " + code);
    }

    var newEntity = new Entity();
    newEntity.setCode(code);
    newEntity.setValue(value);
    em.persist(newEntity);
}

然后(这很重要!)您必须在 @Transactional 注释上为使用此 table 的方法定义 SERIALIZABLE 隔离级别。

阅读更多关于可序列化隔离级别的信息here,他们有一个非常相似的例子。

请注意,您必须考虑重试失败的交易。不过不知道如何用 Spring 做到这一点。

使用交易

最常见和长期接受的方法是以合适的形式(在 class、库、服务等中)简单地抽象出管理您描述的行为的业务规则,在交易中:

@Transactional(propagation = Propagation.REQUIRED)
public RetType operation(ReqType args) {
    ...
    perform operations;
    ...
    if(fail post conditions)
        throw ...;
    ...
}

在这种情况下,如果在调用方法时已经有一个打开的事务,将使用该事务(并且不会有互锁),如果没有创建事务,它将创建一个新事务,以便操作和后置条件检查都在同一事务中执行。

请注意,使用此策略,操作和不变检查事务都可以同时并以协调的方式组合由 TransactionManager(例如 Redis、MySQL、MQS 等)管理的多个事务状态).

仅使用数据库

它已经很久没有使用了(赞成第一种方式)但是使用 TRIGGERS 是几十年前用于检查后置条件的规范选项,但这种解决方案通常与特定的数据库引擎耦合(例如在 PostgreSQL or MySQL).

在进行修改的客户端无法或不愿意(不安全)检查事务中的后置条件(例如 bash 进程)的情况下,它可能很有用。不过现在已经很少见了。

在某些需要效率的情况下,使用 TRIGGERS 也可能更可取,因为数据库脚本中有某些优化选项。

你应该使用单例 (javax/ejb/Singleton)

@Singleton
public class Register {
  @Lock(LockType.WRITE)
  public register(String code, int value) {
    if(i_can_insert_modify(code, value)) {
      //use entityManager or some dao
    } else {
      //do something
    }
  }
}