2011-11-16 55 views
1

我使用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; 
    } 

假设我有几个流可能在同一会话发生:

  1. 得到A.x
  2. 得到整个A实体
  3. 检查是否A包含Bs2="...";

对于这些疑问,我可以

  • 编写获得通过idaA.x/B和特定查询的每一个s2
  • 假设休眠节省缓存,并始终得到A对象,或A.Bset()然后java循环内部获得所需的BBset()

什么是最有效的方法?

谢谢

回答

1

对于用例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中的简单过滤可能会更好。这真的取决于你的架构。

+0

谢谢你,解决了我的一些用例。如果一旦我需要一个B行,我会推荐什么?我需要整个Bset? 我认为,提前抓取会为A.Bset逐一带来B行。 – lili

+0

不,预先提取实际上是用来防止逐个提取(臭名昭着的N + 1问题)。随着渴望的提取,Hibernate会为你创建一个JOIN查询,并用一个SQL同时提取所有'B'的'A'。 –

+0

这是使用@FetchMode(JOIN) –

1

只是一个警告,我不认为A和B之间的上述映射将工作。当你的关系一对多端使用的mappedBy需要在B.

也映射反向关系
@Entity 
class B{ 
     @ManyToOne 
     @JoinColumn(name="aid") 
     private A a; 
     private String s2; 
    } 

一个一对多的单向关系并不推荐使用。

在回答您的问题时,有效使用会话缓存的关键是尽可能使用get()。因此,如果您可以通过它的@Id获得A,则可以遍历映射的B集合以找到您感兴趣的实例。

使用get将确保如果您之后的A实例处于缓存它将被返回(非常快),如果它不在缓存中,它将被添加到缓存中,然后返回给你。然后,下次你通过@Id请求它时,它将从缓存中返回。

每当您使用HQL并强制Hibernate执行查询时,您将绕过会话缓存并直接进入数据库。如果您发现自己需要执行错过缓存的查询,则可能需要查看查询缓存。

+0

进行配置感谢您的回答。你写道:“然后下次你通过@ Id请求它时,它将从缓存中返回。” 我从来没有通过ID列查询,我通过@ UniqueConstraint查询。它仍然会从缓存中返回吗? (equals和hashcode也是基于UniqueConstraint) – lili

+0

我不认为它会。据我所知,会话缓存本质上是一个ID到对象的大图。因此,要从中获取实体,您必须使用Id。 –