OneToMany 关系不起作用

Posted

技术标签:

【中文标题】OneToMany 关系不起作用【英文标题】:OneToMany relationship is not working 【发布时间】:2012-01-08 06:21:30 【问题描述】:

我的表:

产品:ID、名称

Offer:id、value、product_id

实体:

@Entity
@Table(name="product")
public class Product implements Serializable 
    @OneToMany(mappedBy="product")
    private Set<Offer> offers;
    ...


@Entity
@Table(name="offer")
public class Offer implements Serializable 
    @ManyToOne
    @JoinColumn(name="PRODUCT_ID")
    private Product product;
    ...

当我尝试从表 Product 中获取一些数据时,我得到一个 java.lang.NullPointerException,并且此代码:product.getOffers() 返回:

IndirectSet: 未实例化

如何解决这个问题?

【问题讨论】:

你用的是哪个orm框架?? 我使用 JPA 作为 ORM 框架。 Emanuel:我认为@kiki 是在询问您正在使用哪种 JPA 实现。 【参考方案1】:

不是错误消息。打印指令导致在底层IndirectSet 上调用toString()

TopLink 将在实例变量中放置一个 IndirectSet 从数据库中读取包含域对象。与第一个 消息发送到 IndirectSet,内容从 数据库和正常的 Set 行为恢复。

IndirectCollection 类型是专门实现的,不会在 toString() 上实例化:

出于调试目的,#toString() 不会触发数据库读取。

但是,对间接集合的任何其他调用,例如 size() 或 isEmpty() 都会实例化该对象。

当“委托”之一最终触发数据库读取 方法首先调用 getDelegate(),然后调用 buildDelegate(),它将消息 getValue() 发送给值持有者。 值持有者执行数据库读取。与第一个 消息发送到 IndirectSet,内容从 数据库和正常的 Set 行为恢复。

另见IndirectList: not instantiated

【讨论】:

+1 对此回复;非常有用,因为我想知道为什么 eclipselink 间接列表 isEmpty() 会出现在 Java Visual VM 中,因为我在用仅检查 if list != null 和 !list.isEmpty 替换间接列表的 JPA 查询后审查、比较和报告性能时()。这绝对解决了我遇到的性能问题;最初访问数据库超过 600 次,但在最新的实现中,仅访问数据库 5 次! +1'd。虽然答案适用于 TopLink,但它也适用于 EclipseLink。 @gammay EclipseLink 基于 TopLink (fyi)【参考方案2】:

如果您在访问product.getOffers() 时得到IndirectSet: not instantiated,那么您很可能是在事务之外执行此代码。

默认情况下,@OneToMany@ManyToMany 关系为 lazy loaded,这意味着,为了获得更好的性能,只有在第一次访问时才会获取数据。这必须发生在活动事务中。 如果您不在此范围内访问此数据,则您将无法再访问此数据。您应该将调用代码放在活动事务中,或者将集合更改为 eager 而不是惰性:

@OneToMany(mappedBy="product", fetch=FetchType.EAGER)

【讨论】:

我自己用 JUnit 测试进行了测试,我确实环绕了 final EntityTransaction txn = em.getTransaction(); txn.开始();和 txn.commit() 但我仍然得到相同的行为。 所以,您确实在 EntityManager 的事务调用了您的逻辑(并且您确实加载和对象并访问它没有离开 tx)并且您仍然得到同样的异常? 我没有得到“异常”我得到了间接集:未实例化 你是对的——这不是个例;但仍然 - 我的句子的其余部分是否正确? 不这么认为...要么在事务中根据需要加载数据(LAZY 背后的理由),要么急切地加载它,这样你就不需要 tx 来访问它(有效的渴望)。您可以使用 FetchType.EAGER、使用 JPQL JOIN FETCH 或在 tx 中迭代集合的元素来急切地加载对象。尽管如此,所有这些选项都有效地急切地加载整个集合。【参考方案3】:

这就是我所做的

// force load of the set.
entity.getSecrets().isEmpty();
System.out.println(entity.getSecrets());

【讨论】:

为什么要调用entity.getSecrets().isEmpty()?据我所知,它不会加载您的集合,因为您仍然需要访问对象本身;您发布的 sysout 应该可以解决问题(尽管调用对象的 toString 方法只是为了进行延迟加载并不是一个好方法..) 您会这么认为,但 EclipseLink 似乎并非如此。无论如何,它可能是一个“功能”, .isEmpty() 强制加载我找到的集合。【参考方案4】:

这解决了我的问题

@OneToMany(mappedBy = "columnName", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

【讨论】:

【参考方案5】:

我使用 Eclipselink 2.5.2 版本作为我的 ORM 供应商,并且在延迟加载包含实体时,我在一对多 JPA 映射中遇到了这个问题。对我有用的解决方法是:-

final List<ContainingEntity> list = Collections.unmodifiableList(new ArrayList<> 
                                    (containerEntity.getContainingEntityList());

容器实体端的映射是:

@OneToMany(mappedBy = containerEntity, cascade = CascadeType.ALL, fetchType = FetchType.LAZY)
private List<ContainingEntity> containingEntityList;

包含实体端的映射是:

@ManyToOne
@JoinColumn(name = "container_entity_id")
private ContainerEntity containerEntity;

【讨论】:

以上是关于OneToMany 关系不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥单向@OneToMany 更新不起作用

Symfony OneToMany 关联不起作用

如何验证两个实体之间的 OneToMany 关系

KOTLIN orphanRemoval 不起作用

JPA/Hibernate 级联删除不起作用

Java JPA双向ManyToOne映射不起作用