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、使用 JPQLJOIN 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 关系不起作用的主要内容,如果未能解决你的问题,请参考以下文章