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

Posted

技术标签:

【中文标题】如何在使用 Hibernate 保存数据之前检查特殊条件【英文标题】:How to check special conditions before saving data with Hibernate 【发布时间】:2022-01-20 17:49:56 【问题描述】:

示例场景

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

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

id code value
1 A 15
2 A 5
3 B 12
4 B 3
如果我插入 (A,2) 它超出了限制,我想获得例外 如果我插入 (B,4) 事务应该成功,因为它没有超过限制 代码和价值是相互关联的

我能做什么

我可以使用所需的查询来检查这种情况。例如,我为它写了一个方法,我可以在 save 方法中检查它。就是这样。

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

比如设计Entity的时候有注解吗? 我可以不每次都调用提供此控件的方法来执行此操作吗?

我可以举出哪些例子?

@UniqueConstraint 检查是否添加了相同的值

【问题讨论】:

【参考方案1】:

你应该使用单例(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
    
  

【讨论】:

【参考方案2】:

使用交易

最常见且被长期接受的方式是在事务中以合适的形式(在类、库、服务……)中简单地抽象出管理您描述的行为的业务规则:

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

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

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

仅使用数据库

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

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

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

【讨论】:

【参考方案3】:

Hibernate 和 Spring Data 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 注释上为使用此表的方法定义SERIALIZABLE 隔离级别。

阅读更多关于可序列化隔离级别here,他们有一个奇怪的相似示例。

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

【讨论】:

以上是关于如何在使用 Hibernate 保存数据之前检查特殊条件的主要内容,如果未能解决你的问题,请参考以下文章

应用程序在后台时如何检查位置?

HIbernate - 对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例

如何使用 HIbernate 作为 BLOB 将此图像保存到 MySQL 数据库?

如何在firebase中检索子(自动增量)名称

hibernate如何保存blob数据

Hibernate JPA 关联关系