如何获取JPA-ECLIPSELINK中的嵌套实体,以防属于@manytoone

发布于 2025-01-21 13:26:51 字数 3755 浏览 2 评论 0原文

我有多个我想在单个查询中获取的实体,因为n+1查询持续时间太长。 例如,SQL加入查询持续5秒在DB上,但是由于n+1提取,Elcipselink持久性提取持续50-80秒。 我发现,在实现@manytoone关系后,左加入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; 
}

这就是我的获取方式。 我已经尝试了以下所有组合,但是每个组合都会对centerdata中的每一行(对象)进行查询,或者在某些情况下对每种加入的情况data coindata加入centaineRecord。

     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();

I have multiple entities that i want to fetch in single query, because N+1 queries last too long.
For example SQL join query lasts 5 seconds on DB, but elcipselink persistence fetching lasts 50-80 seconds due to N+1 fetching.
I found out that LEFT JOIN FETCH is not working as soon as @ManyToOne relation is implemented.
Does anyone know solution to LEFT JOIN FETCH for this case?

Please find below simplified entities.

 @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; 
}

This is how i fetch it.
I have tried all of the below combinations, but every combinations makes query for each row (object) in SituationData, or in some cases for each joined SituationData join 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();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

别再吹冷风 2025-01-28 13:26:51

由于您没有明确使用Eclipselink版本,因此我只假设版本2.6。

按照 eclipselink.left_fetch 不是受支持的查询提示。您可能正在尝试使用eclipselink.join-fetcheclipselink.left-join-fetch as

使用此提示的正确方法沿着:

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();

另一种方法是批次提取

这将起作用:

//...
em
.createQuery("SELECT ds FROM DatexSituationData ds")
.setHint("eclipselink.batch", "ds.situationRecords")
//...
.setHint("eclipselink.batch.type", "IN")
.setHint("eclipselink.batch_size", "1000");
//...

您可能会测试两种方法并收集一些指标;然后,确定哪个对DB模式和使用模式更好(在性能方面)。

Since you are not explicitly state EclipseLink version being used, I'll just assume version 2.6.

As per the 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.

The proper way to use this hint is along the lines of:

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();

Another approach would be batch fetching.

This would work as:

//...
em
.createQuery("SELECT ds FROM DatexSituationData ds")
.setHint("eclipselink.batch", "ds.situationRecords")
//...
.setHint("eclipselink.batch.type", "IN")
.setHint("eclipselink.batch_size", "1000");
//...

You would probably test both approaches and gather some metrics; then decide which one works better (in terms of performance) for your DB schema and usage patterns.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文