Hibernate @OneToMany 导致多个 select 语句
Hibernate @OneToMany causing multiple select statements
我正在处理一个 Hibernate 问题,它涉及 2 个单独定义的独立实体 bean 类:
- 商店
- StoreServer
请注意,一个 Store 将有多个 StoreServer - 因此使用了 @OneToMany 注释。请看代码片段如下:
商店:
@Entity
@Table(name="Store")
public class Store implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = 5644190852867691168L;
@Id
@Column(name="STORE_NO", nullable=false)
private int storeNumber;
@Column(name="STORE_NAME", nullable=false)
private String storeName;
@Column(name="STORE_PHONE", nullable=false)
private String storePhone;
//other Store fields...
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name="STORE_NO", insertable=false, updatable=false)
private List<StoreServer> storeServers = new ArrayList<StoreServer>();
//getters and setters
商店服务器:
@Entity
@Table(name="Store_Server")
public class StoreServer implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -5410564578856243437L;
@Id
private StoreServerPK storeServerPK;
@Column(name="IP_ADDRESS", nullable=true)
private String ipAddress;
//other StoreServer fields...getters and setters
由于StoreServer有复合主键,这里是StoreServerPK:
@Embeddable
public class StoreServerPK implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -1401889029390423604L;
@Column(name="STORE_NO", nullable=false)
protected int storeNumber;
@Column(name="SERVER_NO", nullable=false)
protected String serverNumber;
//getters and setters
目前,我得到了正确的结果,但性能慢得令人无法接受。我已经打开 Hibernate 中的日志记录,我可以看到每个商店实体正在 运行 单独的 SELECT 查询以获得关联的 StoreServer 记录。
目前,在日志中,我看到一条 SELECT 语句来获取商店记录(返回了 200 多个结果)。然后对于每个商店,一个新的 SELECT 语句来获取 StoreServer 记录。我的问题是...为什么 Hibernate 不进行连接(运行宁一个查询)?
关于如何告诉 Hibernate 使用 JOIN 运行 单个查询,我能得到一些帮助吗?
谢谢
叫做N+1问题
解决方案实际上取决于您如何进行查询 - 如果您使用的是 Criteria API,则应使用 Root.fetch 方法:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Store> cq = qb.createQuery(Store.class);
Root<Store> root = cq.from(Store.class);
root.fetch(App_.storeServers, JoinType.LEFT);
cq.select(root);
return em.createQuery(cq).getResultList();
如果您使用的是 HQL,则应使用 fetch
关键字:
select distinct st from Store st left join fetch st.storeServers
使用内存数据库(如 H2 and JDBC Sniffer
)在单元测试中验证 Hibernate 生成的查询数量可能是个好主意
我正在处理一个 Hibernate 问题,它涉及 2 个单独定义的独立实体 bean 类:
- 商店
- StoreServer
请注意,一个 Store 将有多个 StoreServer - 因此使用了 @OneToMany 注释。请看代码片段如下:
商店:
@Entity
@Table(name="Store")
public class Store implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = 5644190852867691168L;
@Id
@Column(name="STORE_NO", nullable=false)
private int storeNumber;
@Column(name="STORE_NAME", nullable=false)
private String storeName;
@Column(name="STORE_PHONE", nullable=false)
private String storePhone;
//other Store fields...
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name="STORE_NO", insertable=false, updatable=false)
private List<StoreServer> storeServers = new ArrayList<StoreServer>();
//getters and setters
商店服务器:
@Entity
@Table(name="Store_Server")
public class StoreServer implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -5410564578856243437L;
@Id
private StoreServerPK storeServerPK;
@Column(name="IP_ADDRESS", nullable=true)
private String ipAddress;
//other StoreServer fields...getters and setters
由于StoreServer有复合主键,这里是StoreServerPK:
@Embeddable
public class StoreServerPK implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -1401889029390423604L;
@Column(name="STORE_NO", nullable=false)
protected int storeNumber;
@Column(name="SERVER_NO", nullable=false)
protected String serverNumber;
//getters and setters
目前,我得到了正确的结果,但性能慢得令人无法接受。我已经打开 Hibernate 中的日志记录,我可以看到每个商店实体正在 运行 单独的 SELECT 查询以获得关联的 StoreServer 记录。
目前,在日志中,我看到一条 SELECT 语句来获取商店记录(返回了 200 多个结果)。然后对于每个商店,一个新的 SELECT 语句来获取 StoreServer 记录。我的问题是...为什么 Hibernate 不进行连接(运行宁一个查询)?
关于如何告诉 Hibernate 使用 JOIN 运行 单个查询,我能得到一些帮助吗?
谢谢
叫做N+1问题
解决方案实际上取决于您如何进行查询 - 如果您使用的是 Criteria API,则应使用 Root.fetch 方法:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Store> cq = qb.createQuery(Store.class);
Root<Store> root = cq.from(Store.class);
root.fetch(App_.storeServers, JoinType.LEFT);
cq.select(root);
return em.createQuery(cq).getResultList();
如果您使用的是 HQL,则应使用 fetch
关键字:
select distinct st from Store st left join fetch st.storeServers
使用内存数据库(如 H2 and JDBC Sniffer
)在单元测试中验证 Hibernate 生成的查询数量可能是个好主意