如果子实体与@ManyToOne 相关,如何在 JPA-eclipselink 中获取连接嵌套实体
How to fetch join nested entities in JPA-eclipselink, in case of sub-entity being in relation @ManyToOne
我想在单个查询中获取多个实体,因为 N+1 查询持续时间太长。
例如 SQL join query 在 DB 上持续 5 秒,但 elcipselink 持久化抓取由于 N+1 抓取持续 50-80 秒。
我发现一旦实现@ManyToOne 关系,LEFT JOIN FETCH 就无法正常工作。
有谁知道这种情况下 LEFT JOIN FETCH 的解决方案?
请在下面找到简化的实体。
@Entity
@Table(name="SITUATION_DATA")
@NamedQuery(name="SituationData.findAll", query="SELECT s FROM SituationData s")
public class DatexSituationData implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="datexSituationData", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecord> situationRecords;
}
@Entity
@Table(name="SituationRecord")
@NamedQuery(name="SituationRecord.findAll", query="SELECT s FROM SituationRecord s")
public class SituationRecord implements Serializable {
private static final long serialVersionUID = 1L;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordComment> situationRecordComment;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordTypeElement> situationRecordTypeElements;
//bi-directional many-to-one association to SituationLocation
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
@JoinColumn(name="ID_LOKACIJE")
private SituationLocation situationLocation;
//bi-directional many-to-one association to DatexSituationData
@ManyToOne()
@JoinColumns({
@JoinColumn(name="SITUATION_ID", referencedColumnName="ID", nullable=false),
@JoinColumn(name="SITUATION_VERSION", referencedColumnName="VERSION", nullable=false)
})
private DatexSituationData datexSituationData;
}
@Entity
@Table(name="SITUATION_LOCATIONS")
@NamedQuery(name="SituationLocation.findAll", query="SELECT s FROM SituationLocation s")
public class SituationLocation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="situation_location_seq")
@SequenceGenerator(name="situation_location_seq", sequenceName="SEQ_SITUATION_LOCATION", allocationSize=1)
@Column(name="ID_LOKACIJE", unique=true, nullable=false)
private long idLokacije;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="situationLocation", cascade = CascadeType.PERSIST)
private List<SituationRecord> situationRecords;
}
这就是我获取它的方式。
我已经尝试了以下所有组合,但每种组合都会查询 SituationData 中的每一行(对象),或者在某些情况下查询每个加入的 SituationData 加入 SituationRecord。
String sQuery =
//"select * from SITUATION_DATA t";
//"SELECT * FROM (select t.*, rank() over(partition by t.id order by version desc) rnk from SITUATION_DATA t) where rnk = 1";
"SELECT ds FROM SituationData ds LEFT JOIN FETCH ds.situationRecords sr LEFT JOIN FETCH sr.situationLocation sl LEFT JOIN FETCH sr.situationRecordTypeElements sre LEFT JOIN FETCH sr.situationRecordComment src";
EntityManager em = Emf.getInstance().getFactory().createEntityManager();
//Query q = em.createNativeQuery(sQuery, DatexSituationData.class);
Query q = em.createQuery(sQuery, DatexSituationData.class);
// q.setHint("eclipselink.LEFT_FETCH", "t.situationRecords.situationRecordComment");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.sl");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.sre");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.src");
// q.setHint("eclipselink.JDBC_FETCH_SIZE", "100");
lResult = q.getResultList();
由于您没有明确说明所使用的 EclipseLink 版本,因此我假设使用的是 2.6 版。
根据 official EclipseLink documentation, eclipselink.LEFT_FETCH
is not a supported query hint. You are probably trying to use eclipselink.join-fetch
or eclipselink.left-join-fetch
as documented here.
使用此提示的正确方法是:
String sQuery = "SELECT ds FROM DatexSituationData ds";
EntityManager em = emf.getInstance().getFactory().createEntityManager();
TypedQuery q = em.createQuery(sQuery, DatexSituationData.class);
q.setHint("eclipselink.join-fetch", "ds.situationRecords");
q.setHint("eclipselink.join-fetch", "ds.situationLocation");
// ...
lResult = q.getResultList();
另一种方法是 batch fetching。
这可以作为:
//...
em
.createQuery("SELECT ds FROM DatexSituationData ds")
.setHint("eclipselink.batch", "ds.situationRecords")
//...
.setHint("eclipselink.batch.type", "IN")
.setHint("eclipselink.batch_size", "1000");
//...
您可能会测试这两种方法并收集一些指标;然后决定哪一个更适合您的数据库模式和使用模式(就性能而言)。
我想在单个查询中获取多个实体,因为 N+1 查询持续时间太长。 例如 SQL join query 在 DB 上持续 5 秒,但 elcipselink 持久化抓取由于 N+1 抓取持续 50-80 秒。 我发现一旦实现@ManyToOne 关系,LEFT JOIN FETCH 就无法正常工作。 有谁知道这种情况下 LEFT JOIN FETCH 的解决方案?
请在下面找到简化的实体。
@Entity
@Table(name="SITUATION_DATA")
@NamedQuery(name="SituationData.findAll", query="SELECT s FROM SituationData s")
public class DatexSituationData implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="datexSituationData", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecord> situationRecords;
}
@Entity
@Table(name="SituationRecord")
@NamedQuery(name="SituationRecord.findAll", query="SELECT s FROM SituationRecord s")
public class SituationRecord implements Serializable {
private static final long serialVersionUID = 1L;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordComment> situationRecordComment;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordTypeElement> situationRecordTypeElements;
//bi-directional many-to-one association to SituationLocation
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
@JoinColumn(name="ID_LOKACIJE")
private SituationLocation situationLocation;
//bi-directional many-to-one association to DatexSituationData
@ManyToOne()
@JoinColumns({
@JoinColumn(name="SITUATION_ID", referencedColumnName="ID", nullable=false),
@JoinColumn(name="SITUATION_VERSION", referencedColumnName="VERSION", nullable=false)
})
private DatexSituationData datexSituationData;
}
@Entity
@Table(name="SITUATION_LOCATIONS")
@NamedQuery(name="SituationLocation.findAll", query="SELECT s FROM SituationLocation s")
public class SituationLocation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="situation_location_seq")
@SequenceGenerator(name="situation_location_seq", sequenceName="SEQ_SITUATION_LOCATION", allocationSize=1)
@Column(name="ID_LOKACIJE", unique=true, nullable=false)
private long idLokacije;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="situationLocation", cascade = CascadeType.PERSIST)
private List<SituationRecord> situationRecords;
}
这就是我获取它的方式。 我已经尝试了以下所有组合,但每种组合都会查询 SituationData 中的每一行(对象),或者在某些情况下查询每个加入的 SituationData 加入 SituationRecord。
String sQuery =
//"select * from SITUATION_DATA t";
//"SELECT * FROM (select t.*, rank() over(partition by t.id order by version desc) rnk from SITUATION_DATA t) where rnk = 1";
"SELECT ds FROM SituationData ds LEFT JOIN FETCH ds.situationRecords sr LEFT JOIN FETCH sr.situationLocation sl LEFT JOIN FETCH sr.situationRecordTypeElements sre LEFT JOIN FETCH sr.situationRecordComment src";
EntityManager em = Emf.getInstance().getFactory().createEntityManager();
//Query q = em.createNativeQuery(sQuery, DatexSituationData.class);
Query q = em.createQuery(sQuery, DatexSituationData.class);
// q.setHint("eclipselink.LEFT_FETCH", "t.situationRecords.situationRecordComment");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.sl");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.sre");
q.setHint("eclipselink.LEFT_FETCH", "ds.sr.src");
// q.setHint("eclipselink.JDBC_FETCH_SIZE", "100");
lResult = q.getResultList();
由于您没有明确说明所使用的 EclipseLink 版本,因此我假设使用的是 2.6 版。
根据 official EclipseLink documentation, eclipselink.LEFT_FETCH
is not a supported query hint. You are probably trying to use eclipselink.join-fetch
or eclipselink.left-join-fetch
as documented here.
使用此提示的正确方法是:
String sQuery = "SELECT ds FROM DatexSituationData ds";
EntityManager em = emf.getInstance().getFactory().createEntityManager();
TypedQuery q = em.createQuery(sQuery, DatexSituationData.class);
q.setHint("eclipselink.join-fetch", "ds.situationRecords");
q.setHint("eclipselink.join-fetch", "ds.situationLocation");
// ...
lResult = q.getResultList();
另一种方法是 batch fetching。
这可以作为:
//...
em
.createQuery("SELECT ds FROM DatexSituationData ds")
.setHint("eclipselink.batch", "ds.situationRecords")
//...
.setHint("eclipselink.batch.type", "IN")
.setHint("eclipselink.batch_size", "1000");
//...
您可能会测试这两种方法并收集一些指标;然后决定哪一个更适合您的数据库模式和使用模式(就性能而言)。