我们在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")
}
因此,您可以看到SubEntity
与MainEntity
的关系由两个属性表示,其中mainId
属性负责管理关系/外键。
这工作得很好,完全符合我们的需要。
然而,急切加载SubEntity
和MainEntity
有一个问题。
假设我有一个查询,返回MainEntity
的集合。在当前的设置中,Hibernate将发出n + 1个select:查询本身为每个SubEntity
+ n个select。
当然,我可以在查询中添加join fetch
,但我更希望Hibernate自动执行此操作。因此,我尝试添加@Fetch( FetchMode.JOIN )
,但这没有做任何事情。
我使用@Fetch( FetchMode.SUBSELECT )
也没有问题,它应该将选择语句减少到2 -原始查询和子实体的选择(至少这是在@CollectionOfElements
和@Fetch( FetchMode.SUBSELECT )
注释的另一个属性上发生的事情)。
所以问题是:我如何告诉Hibernate 自动 join fetch或使用单个select来急切加载子实体?我错过什么了吗?
提前感谢,
PS:一件事可能是一个问题可能是mappedBy = "main"
不引用实际id列,但我不能改变它为mappedBy = "id"
。
如果您想在MainEntity和SubEntity之间共享主键,请使用PrimaryKeyJoinColumn
和MapsId
注释。
通过使用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
有三个选项可以避免n +1的问题:
Lot size
subselect
Make a LEFT JOIN in the query
FAQ1这里FAQ2