JPA OneToMany - Collection为null

use*_*511 4 java jpa nullpointerexception

我正在尝试使用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);
  }
  ..
}
Run Code Online (Sandbox Code Playgroud)

图书实体是:

@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);
  }
  ..
}
Run Code Online (Sandbox Code Playgroud)

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

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

JB *_*zet 7

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

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

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

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

  • 请注意,最后一部分并非完全正确.如果在事务之外调用getter,JPA会将您的集合重置为null,即使您在构造函数中初始化它也是如此.如果您有长时间运行的进程并且不想让事务处于打开状态,请确保在离开事务之前显式调用getter. (2认同)
  • 是的,我投了反对票。不,问题和我的一样。你不认为你与我引用的两个陈述相矛盾吗?您如何看待@MouseLearnJava 的回答?... PS:我已经尝试撤消我的downvote,但stackoverflow不会让我 (2认同)
  • 如果书籍集合是惰性的,它不知道要在集合中放入什么,但它仍然知道图书馆有 0-N 本书的集合,因此它使用未初始化的集合代理来初始化 books 字段。第一次在集合上调用任何方法时,将从数据库加载书籍并填充集合,或者如果实体已分离,则抛出 LazyInitializationException。 (2认同)

Mou*_*ava 6

看来,Library类中的Collection类型的书籍类型没有被启动.它是null;

所以当类addBook方法将book对象添加到集合中时.它会导致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);
 }
Run Code Online (Sandbox Code Playgroud)

启动它并试一试.

更改

private Collection<Book> books;
Run Code Online (Sandbox Code Playgroud)

private Collection<Book> books = new ArrayList<Book>();
Run Code Online (Sandbox Code Playgroud)