我使用JPA与Hibernate实现。我的问题可能是有经验的hibernate用户的基本问题:基于hibernate一级缓存编写查询的最有效方法是什么?
例如,我有实体A
和实体B
:
@Entity
class A{
private int ida;
private int x;
private String s;
@OneToMany(mappedBy = "ida", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<B> Bset;
}
@Entity
class B{
private int ida;
private String s2;
}
假设我有几个流可能发生在同一个会话中:
- get
A.x
- 获取整个
A
实体 - 检查
A
是否包含B
和s2="..."
;
对于这些查询,我可以
- 编写一个特定的查询,通过
ida
和s2
获取A.x
/B
或 - 假设Hibernate保存缓存并且总是获取
A
对象,或者A.Bset()
,然后java循环在Bset()
中获取所需的B
最有效的方法是什么?
谢谢
对于用例1和2,我只加载整个实体。第二级缓存也同样有效。除非你的A
对象非常大(有很多属性),否则与
SELECT a.x FROM A a WHERE a.id = :id
更糟糕的是,使用上面的查询不会利用二级缓存。
第三个用例更有趣。这在很大程度上取决于您的需求,但合理的平衡是使用这样的查询:
SELECT B b
WHERE b.s2 == :s2
AND b.a = :a
如果查询返回一些东西,这意味着a
包含b
与给定的s2
。这应该比惰性加载Bset
并迭代它要快得多。考虑启用查询缓存。
但是,如果Bset
通常很小,并且您使用即时抓取,那么在Java中进行简单的过滤可能会更好。这取决于你的架构。
只是一个警告,我不认为上面的a和B之间的映射将工作。当您在关系的OneToMany端使用mapappedby时,您还需要映射b中的反向关系
@Entity
class B{
@ManyToOne
@JoinColumn(name="aid")
private A a;
private String s2;
}
不建议使用OneToMany的单向关系。
回答你的问题,有效使用会话缓存的关键是尽可能使用get()。因此,如果你可以通过A的@Id得到A,你就可以遍历B的映射集合来找到你感兴趣的实例。
使用get将确保如果A的实例在缓存中,它将被返回(非常快),如果它不在缓存中,它将被添加到缓存中,然后返回给你。然后,下次您通过@Id请求它时,它将从缓存返回。
当你使用HQL并强制Hibernate执行查询时,你将绕过Session缓存而直接进入数据库。如果你发现自己需要执行没有缓存的查询,那么查询缓存可能是值得研究的。