【问题标题】:OneToMany relationship is not workingOneToMany 关系不起作用
【发布时间】: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 实现。

标签: java jpa entity one-to-many


【解决方案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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      • 1970-01-01
      • 2017-05-22
      • 2014-08-02
      相关资源
      最近更新 更多