如何在使用 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 |
我能做什么
我可以使用所需的查询来检查这种情况。例如,我为它写了一个方法,我可以在 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 - 对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例