Hibernate OneToOne自动连接提取(解决n + 1问题)

时间:2011-08-11 12:09:08

标签: java hibernate jpa

我们对Hibernate 3.3有一个n + 1选择问题。

为简单起见,我只做一个简短的抽象例子。

假设我们有以下简单类:

class MainEntity {
  @Id
  public Long id; //we have a table generator create this id

  @OneToOne ( mappedBy ="main" )
  public SubEntity subEntity;
}

class SubEntity {
 @Id
 @Column( name = "mainId" ) //note that this is the same column as the join column below
 public Long mainId; //in order to have the exact same id as the corresponding MainEntity

 @OneToOne ( fetch = FetchType.LAZY )
 @JoinColumn ( name = "mainId", insertable = false, updatable = false, nullable = false )
 public MainEntity main; //this is used for navigation and queries (" ... subentity.main = :x")
}

因此,您可以看到SubEntityMainEntity的关系由两个属性表示,其中mainId属性是负责管理关系/外键的属性。< / p>

这非常有效,完全符合我们的需求。

然而,急切加载SubEntityMainEntity会产生一个问题。

假设我有一个返回MainEntity集合的查询。使用当前设置,Hibernate将发出n + 1个选择:查询本身+ n为每个SubEntity选择。

当然我可以在查询中添加join fetch,但我更喜欢Hibernate自动执行此操作。因此我尝试添加@Fetch( FetchMode.JOIN ),但这没有做任何事情。

我也可以使用@Fetch( FetchMode.SUBSELECT )没有问题,这应该将select语句减少到2 - 原始查询和子实体的选择(至少那是在{{1}注释的另一个属性上发生的事情和@CollectionOfElements)。


所以问题是:我如何告诉Hibernate 自动加入fetch或使用单个select来急切加载子实体?我错过了什么吗?

提前谢谢,

托马斯

PS:可能存在问题的一件事可能是@Fetch( FetchMode.SUBSELECT )没有引用实际的id列,但我无法将其更改为mappedBy = "main"

2 个答案:

答案 0 :(得分:8)

如果要在MainEntity和SubEntity之间共享主键,请使用PrimaryKeyJoinColumnMapsId注释。

通过使用PrimaryKeyJoinColumn加载实体  通过使用相同的主键将MainEntity表与SubEntity表连接起来。它应解决n + 1问题。

MapsId注释要求Hibernate从中复制标识符 我们示例中的另一个关联实体会将SubEntity.mainEntity.id复制到SubEntity.id

@Entity
public class MainEntity {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "main_Id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private SubEntity  subEntity ;
}


@Entity
public class SubEntity 
{
    @Id @Column(name="main_Id_FK") Long id;

    @MapsId 
    @OneToOne
    @JoinColumn(name = "main_Id_FK")    
    @PrimaryKeyJoinColumn
    private MainEntity mainEntity;        

}

Hibernate参考文档:

PrimaryKeyJoinColumn
  MapsId

答案 1 :(得分:1)

有三种方法可以避免问题n +1:

 Lot size

 subselect

 Make a LEFT JOIN in the query

Here FAQ1 Here FAQ2