java JPA。如何返回null而不是LazyInitializationException
我有两张“一对多”关系的桌子。我使用Jpa+Spring Jpa。有时我必须从数据库中获取具有内部对象的对象。有时候我不必这么做。存储库总是返回带有内部对象的对象。 我试着从数据库中获取“所有者”,我总是得到固定的书籍;没关系。但当我阅读这本内部书籍的字段时,我得到了LazyInitializationException。如何获取null而不是异常
@Entity
@Table(name = "owners")
@NamedEntityGraph(name = "Owner.books",
attributeNodes = @NamedAttributeNode("books"))
public class Owner implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "owner_id", nullable = false, unique = true)
private Long id;
@Column(name = "owner_name", nullable = false)
private String name;
@OneToMany(fetch = FetchType.LAZY,mappedBy = "owner")
private Set<Book> books= new HashSet<>(0);
public Worker() {
}
}
@Entity
@Table(name = "books")
@NamedEntityGraph(name = "Book.owner",
attributeNodes = @NamedAttributeNode("owner"))
public class Book implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "book_id", unique = true, nullable = false)
private Long id;
@Column(name = "book_name", nullable = false, unique = true)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner_id")
private Owner owner;
public Task() {
}
}
public interface BookRepository extends JpaRepository<Book,Long>{
@Query("select t from Book t")
@EntityGraph(value = "Book.owner", type = EntityGraph.EntityGraphType.LOAD)
List<Book> findAllWithOwner();
@Query("select t from Book t where t.id = :aLong")
@EntityGraph(value = "Book.owner", type = EntityGraph.EntityGraphType.LOAD)
Book findOneWithOwner(Long aLong);
}
# 1 楼答案
您获得
LazyInitializationException
是因为您正在访问事务上下文之外的书籍集的内容,很可能是因为它已经关闭。例如:通过DAO或Spring数据存储库,在服务类的方法中从数据库中获得所有者:
此时,您有了一个所有者对象,其图书集为空,只有当有人想要访问其内容时才会填充。只有在存在打开的事务时,才能填充图书集。不幸的是,
findOne
方法已经打开并关闭了事务,因此没有打开的事务,当您执行类似owner.getBooks().size()
的操作时,您将得到臭名昭著的LazyInitializationException
你有几个选择:
使用@Transactional
正如OndrejM所说,您需要以一种在同一事务中执行的方式包装代码。最简单的方法是使用Spring的@Transactional注释:
使用fetch=FetchType。急切的
在
fetch = FecthType.LAZY
定义中有@Column
,这就是为什么要延迟加载集合(如果没有指定,这也是JPA默认使用的获取类型)。如果希望在从数据库中获取所有者对象后立即自动填充集合,则应如下定义:如果
Book
实体不是很重,而且每个Owner
都没有大量的书籍,那么从数据库中取出该所有者的所有书籍并不构成犯罪。但是您还应该注意,如果您检索Owner
列表,那么您也在检索所有这些所有者的所有书籍,并且Book
实体可能正在加载它所依赖的其他对象# 2 楼答案
LazyInitializationException
的目的是在加载的实体与数据库失去连接,但尚未加载现在请求的数据时引发错误。默认情况下,一个实体内的所有集合都是延迟加载的,即在请求时加载,通常是通过对它们调用一个操作(例如size()或isEmpty())您应该包装调用存储库的代码,然后在单个事务中使用该实体,以便该实体在事务完成之前不会断开与DB的连接。如果您不这样做,存储库将自己创建一个事务来加载数据,并在之后立即关闭该事务。然后返回的实体没有事务,并且不可能判断ots集合是否有一些元素。而是抛出
LazyInitializationException