繁体   English   中英

JPA惰性获取列表在setter中调用SELECT查询

[英]JPA lazy fetch list invokes SELECT queries in setter

有一种方法可以通过JPA从数据库返回实体。 该实体具有其他实体的列表,提取类型为LAZY。 当我想将对象添加到此列表时,出现异常:

Caused by: Exception [EclipseLink-7242] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session.  This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization.  To avoid this issue, instantiate the LAZY relationship prior to serialization.

因此,为了克服这个问题,我可以通过在其上执行.size()来初始化此列表。 问题是我真的不需要从数据库中获取这些对象,所以我想做这样的事情: fetchedEntity.setMyLazyFetchList(new ArrayList<>()); 效果很好。 我可以进一步访问列表,但是问题如下:set方法调用与fetchedEntity.getMyLazyFetchList().size()相同的选择查询。 当我将值设置为新列表时,这些查询无用,为什么要调用它们呢?

方法获取实体

public Competitor findAndInitializeEmptyGroups(Integer idCompetitor) {
    Competitor entity = em.find(Competitor.class, idCompetitor);
    System.out.println("Before set ");
    entity.setGroupCompetitorList(new ArrayList<>());
    System.out.print("After set lazy list size ");
    System.out.print(entity.getGroupCompetitorList().size());

    return entity;
}

实体中的延迟获取列表字段(竞争对手)

@OneToMany(mappedBy = "idCompetitor")
private List<GroupCompetitor> groupCompetitorList = new ArrayList<>();

第二端关系字段(GroupCompetitor)

@JoinColumn(name = "id_competitor", referencedColumnName = "id_competitor")
@ManyToOne(optional = false)
private Competitor idCompetitor;

什么日志说:

Info:   Before set
Fine:   SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine:   SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs

Info:   After set lazy list size
Info:   0

更换线后

entity.setGroupCompetitorList(new ArrayList<>());

entity.getGroupCompetitorList().size();

和日志(除了列表现在由获取的实体组成之外,它们是相同的):

Info:   Before set
Fine:   SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine:   SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs

Info:   After set lazy list size
Info:   44

所以我的问题是:为什么当我执行entity.setGroupCompetitorList(new ArrayList<>());时为什么要调用SELECT查询entity.setGroupCompetitorList(new ArrayList<>()); 由于性能原因,我不希望它们出现。 有什么方法可以消除此问题,或者究竟是什么导致此现象?

使用:

  • EclipseLink JPA 2.1
  • GlassFish 4.1
  • Java 8

如果要添加元素并使JPA提供程序将其保留,则不能获取作为实体成员的列表。 JPA提供者必须跟踪所有者,所有者并处理任何级联(我看不到您已定义,但是我怀疑级联选项的每种组合都有不同的代码路径)。 最简单的方法是将列表保存在内存中,然后决定在提交/刷新时对数据库执行哪些操作。

我认为您最初遍历LAZY的异常的原因是由于在托管上下文中进行外部访问。 从EJB方法返回后,要返回的实体将分离。 您必须将其重新附加到另一个EntityManager或确保在离开方法之前已加载了要使用的所有惰性关系。 调用fetchedEntity.getMyLazyFetchList().size()将是一个示例,并且在单个实体的情况下可以正常工作。 如果要在实体列表中强制加载LAZY,建议您阅读LEFT JOIN FETCH子句。 我在这里假设您的findAndInitializeEmptyGroups()方法位于EJB中,根据我认为该方法中注入的EntitManager em的判断,这些方法将获得默认的@TransactionAttribute(REQUIRED)处理,因为我没有看到相反的注释。

现在,让我们回到您原来的问题:

我想向该列表添加对象

您要解决的问题是将元素添加到列表中而不获取整个列表。 您正在使用mappedBy属性,这意味着您已经创建了双向关系。 如果getGroupCompetitorList()返回无序列表(在Hibernate中为“ bag”),则您根本不必加载该列表。 尝试这样的事情:

GroupCompetitorInteger idCompetitor更改为@ManyToOne(fetch=FetchType.LAZY) Competitor competitor 相应地调整吸气剂和吸气剂。

CompetitorgroupCompetitorList mappedBy为更改为competitor 添加获取/设置方法。

然后,您可以使用EJB中的如下方法从子端将其添加到列表中:

public void addNewGroupCompetitorToCompetitor(Competitor comp, GroupCompetitor gComp) {
    gComp.setCompetitor(comp);
    em.persist(gComp);
    em.flush();
}

下次再次获取Competitor时,遍历entity.getGroupCompetitorList() (由EntityManager管理)时,它应该具有已添加的新GroupCompetitor 这种情况会变得更加复杂,具体取决于comp是否是尚未持久的新实体,但这是基本思想。 为了与EclipseLink一起正常工作,可能需要进行一些调整,但是我对Hibernate进行的操作与JPA提供程序相同,并且可以正常工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM