如何在使用 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
- 如果我插入 (A,2) 它超出了限制,我想得到异常
- 如果我插入 (B,4) 交易应该会成功,因为它没有超过限制
- 代码和值是相互关联的
我能做什么
我可以用所需的查询来检查这个场景。比如我给它写一个方法,我可以在save方法中查看。就是这样。
但是,我正在寻找比这更有用的解决方案
- 比如在设计Entity的时候有注解吗?
- 我可以不用每次都调用提供这个控件的方法来做到这一点吗?
我可以举什么例子?
@UniqueConstraint
检查它是否添加相同的值
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
}
}
}
示例场景
我有一个控制列总值的限制。如果我进行了超过此限制的保存,我希望它抛出异常。例如;
假设我已经添加了以下数据:LIMIT = 20
id | code | value |
---|---|---|
1 | A | 15 |
2 | A | 5 |
3 | B | 12 |
4 | B | 3 |
- 如果我插入 (A,2) 它超出了限制,我想得到异常
- 如果我插入 (B,4) 交易应该会成功,因为它没有超过限制
- 代码和值是相互关联的
我能做什么
我可以用所需的查询来检查这个场景。比如我给它写一个方法,我可以在save方法中查看。就是这样。
但是,我正在寻找比这更有用的解决方案
- 比如在设计Entity的时候有注解吗?
- 我可以不用每次都调用提供这个控件的方法来做到这一点吗?
我可以举什么例子?
@UniqueConstraint
检查它是否添加相同的值
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
}
}
}