【问题标题】:JPA OneToMany - Collection is nullJPA OneToMany - 集合为空
【发布时间】:2013-11-17 12:42:00
【问题描述】:

我正在尝试使用 JPA 建立双向关系。我了解维护双方关系是应用程序的责任。

例如,图书馆有多本书。在图书馆实体中,我有:

@Entity
public class Library {
  ..
  @OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
  private Collection<Book> books;

  public void addBook(Book b) {
    this.books.add(b);
    if(b.getLibrary() != this)
      b.setLibrary(this);
  }
  ..
}

图书实体是:

@Entity
public class Book {
  ..
  @ManyToOne
  @JoinColumn(name = "LibraryId")
  private Library library;

  public void setLibrary(Library l) {
    this.library = l;
    if(!this.library.getBooks().contains(this))
      this.library.getBooks().add(this);
  }
  ..
}

不幸的是,OneToMany 端的集合为空。例如,调用 setLibrary() 会失败,因为 this.library.getBooks().contains(this) 会导致 NullPointerException。

这是正常行为吗?我应该自己实例化集合(这似乎有点奇怪),还是有其他解决方案?

【问题讨论】:

    标签: java jpa nullpointerexception


    【解决方案1】:

    实体是 Java 对象。 Java的基本规则不会因为类上有@Entity注解而改变。

    因此,如果您实例化一个对象并且其构造函数没有初始化其中一个字段,则该字段将被初始化为 null。

    是的,您有责任确保构造函数初始化集合,或者确保所有方法都处理字段的可空性。

    如果您从数据库中获取此实体的实例(使用 em.find()、查询或通过附加实体的关联进行导航),则集合永远不会为空,因为 JPA 将始终初始化集合。

    【讨论】:

    • 请注意,最后一部分并不完全正确。如果您在事务之外调用 getter,JPA 会将您的集合重置为 null,即使您在构造函数中对其进行了初始化也是如此。如果您有长时间运行的进程并且不想让事务保持打开状态,请确保在离开事务之前显式调用 getter。
    • 未初始化或 null - 我得到一个 NullpointerException。 @JBNizet 您写道,“确保构造函数初始化集合是您的责任”,然后您说“JPA 将始终初始化集合”。
    • 是的,我投了反对票。不,问题和我的一样。你不认为你与我引用的两个陈述自相矛盾吗?您如何看待@MouseLearnJava 的答案? ... PS:我已经尝试撤消我的downvote,但stackoverflow不会让我
    • 不,我不知道。如果只是执行Library lib = new Library(),则完全不涉及JPA。你有一个类,你创建了一个实例。这将根据您编写的构造函数中包含的说明初始化字段。另一方面,当您执行Library lib = em.find(Library.class, 42L) 时,就会涉及到 JPA。它在数据库中找到行,调用 Library 的无参数构造函数,然后使用反射从数据库中获取的数据填充其所有非瞬态字段。
    • 如果藏书是惰性的,它不知道在藏书里放什么,但它仍然知道一个图书馆有0-N本书的藏书,所以它初始化了books字段使用未初始化的集合代理。第一次在集合上调用任何方法时,将从数据库加载书籍并填充集合,如果实体已分离,则抛出 LazyInitializationException。
    【解决方案2】:

    似乎图书馆类中收藏的书籍类型未初始化。为空;

    所以当类 addBook 方法添加一个书籍对象到集合中。它会导致 NullPointerException。

    @OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
     private Collection<Book> books;
    
     public void addBook(Book b) {
        this.books.add(b);
        if(b.getLibrary() != this)
        b.setLibrary(this);
     }
    

    初始化它并尝试一下。

    改变

    private Collection<Book> books;
    

    private Collection<Book> books = new ArrayList<Book>();
    

    【讨论】:

      【解决方案3】:

      尝试在 OneToMany 端将 fetch 类型关联属性设置为 eager。实际上,您可以将这部分 (this.library.getBooks().add(this)) 留在会话中:

      Library l = new Library();    
      Book b = new Book();
      b.setLibrary(l);
      l.getBooks().add(b);
      

      【讨论】:

        猜你喜欢
        • 2014-02-03
        • 2012-04-11
        • 1970-01-01
        • 1970-01-01
        • 2017-04-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多