Java 使用Hibernate验证实体存在的最佳实践
在我的一个应用程序中,我有一个验证器,负责检查数据库中是否存在具有给定类和id的实体。在过去,我使用entityManager.find()并简单地检查结果是否为null。虽然这是可行的,但对于具有复杂关联的实体来说,这是昂贵的,因为find()将尝试拉回整个实体图。如果在事务范围之外使用,它也很容易出现延迟加载异常 我一直在寻找另一种解决方案,我发现了一些建议,但我不知道如何从性能和潜在的不利方面来评估它们。根据我迄今为止所做的研究,以下是我看到的潜在选择:Java 使用Hibernate验证实体存在的最佳实践,java,hibernate,jpa,entitymanager,Java,Hibernate,Jpa,Entitymanager,在我的一个应用程序中,我有一个验证器,负责检查数据库中是否存在具有给定类和id的实体。在过去,我使用entityManager.find()并简单地检查结果是否为null。虽然这是可行的,但对于具有复杂关联的实体来说,这是昂贵的,因为find()将尝试拉回整个实体图。如果在事务范围之外使用,它也很容易出现延迟加载异常 我一直在寻找另一种解决方案,我发现了一些建议,但我不知道如何从性能和潜在的不利方面来评估它们。根据我迄今为止所做的研究,以下是我看到的潜在选择: 手动创建并执行“计数”查询 使用条
EntityManager.find(id)
如果实体没有急切的关联,并且大小合理(即不包含大的blob或长字符串),那么它就完全足够了。由于主键索引通常是聚集的,因此加载实体的状态不会导致数据库的额外I/O,并且会产生微不足道的CPU和带宽开销。从积极的一面来看,EntityManager.find(id)
易于使用、易于理解,并且通过将实体加载到会话中,可以加快将来对该实体的访问
如果您的实体非常大,或者具有急切的关联,则计数查询更有效。无论您是用SQL还是通过Criteria API编写,性能影响都可以忽略不计,因为瓶颈通常是数据库I/O,而不是应用程序服务器上的CPU时间
有时,也可能忽略对存在性的检查,并信任数据库在违反约束时向您发出警告。这对于数据库来说更有效,并且在面对高并发性时更健壮。这就是entityManager.getReference(id)
的作用:
person.getFriends().add(entityManager.getReference(newFriendId));
// assume the friend id is valid - if it isn't, the database will tell us anyway
附录:存在性检查和幻影读数
在读提交隔离级别中,另一个事务可以在查询检查实体是否存在后,但在事务提交之前删除实体:
Thread 1 Thread 2
select count(*)
from BigFatEntity
where id = 42;
1 row found
delete from BigFatEntity
where id = 42;
commit;
insert into Relationship(id, big_fat_entity_id)
values (123, 42);
constraint violation: 42 no longer exists
也就是说,在将实体用作外键之前检查是否存在并不能在所有情况下防止外键冲突
即使在可序列化事务隔离下,在这种情况下,先检查也不会阻止事务回滚
也就是说,检查是否存在不会捕获所有情况,但会使事务更长,从而增加事务回滚的概率并降低系统的总体吞吐量。您不能使用
entityManager.getReference()
。它不是为这个目的而设计的。请参阅:当应用程序试图以任何需要访问其数据的方式使用返回的代理时,如果给定实体未引用实际数据库状态,则稍后将引发异常。选项3的变体是否可行?您可以创建一个实体图(),只指定主键和任何可能有用的基本映射。这将强制DB命中并构建一个实体,但开销最小,并允许该引用实体在以后需要时仍用于获取其余的实体-但如果仍在上下文之外访问这些值,hibernate将抛出异常。否则,只需使用构造函数查询返回只包含您想要填充的内容的对象实例,其余的将为null,而不会抛出异常。EntityGraph很有趣,我将为将来的项目深入研究。不过,在这种情况下,与简单地执行计数查询相比,这可能有些过分。