JPA。如何返回 null 而不是 LazyInitializationException

2024-04-13

我有两个具有“一对多”关系的表。我使用 Jpa + Spring JpaRepository。有时我必须使用内部对象从数据库获取对象。有时我不必这样做。存储库总是返回带有内部对象的对象。 我尝试从数据库中获取“所有者”,但总是得到 Set books;没关系。但是当我阅读这本内部 Book 的字段时,我得到了 LazyInitializationException。如何获取 null 而不是 Exception?

@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);
}

你正在得到LazyInitializationException因为您正在访问事务上下文之外的书籍内容,很可能是因为它已经关闭。例子:

您可以在 Service 类的方法中使用 DAO 或 Spring Data 存储库从数据库获取 Owner:

public Owner getOwner(Integer id) {
    Owner owner = ownerRepository.findOne(id);
    // You try to access the Set here
    return owner;
}

此时,您有一个 Owner 对象,其中有一个为空的 books Set,并且仅当有人想要访问其内容时才会填充。仅当存在未完成的交易时才能填充账本集。不幸的是,findOne方法已经打开并且已经关闭了交易,所以没有打开的交易,你会得到臭名昭著的LazyInitializationException当你做类似的事情时owner.getBooks().size().

您有几个选择:

使用@Transactional

正如 OndrejM 所说,您需要以在同一事务中执行的方式包装代码。最简单的方法是使用 Spring 的@事务性 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html注解:

@Transactional
public Owner getOwner(Integer id) {
    Owner owner = ownerRepository.findOne(id);
    // You can access owner.getBooks() content here because the transaction is still open
    return owner;
}

使用 fetch = FetchType.EAGER

你有fetch = FecthType.LAZY in you @Column定义,这就是为什么 Set 被延迟加载(如果没有指定,这也是 JPA 默认使用的获取类型)。如果您希望在从数据库获取 Owner 对象后立即自动完全填充 Set,您应该像这样定义它:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "owner")
private Set<Book> books= new HashSet<Book>();

If the Book实体不是很重并且每个Owner没有大量的书籍,从数据库中取出该所有者的所有书籍并不是犯罪。但您还应该注意,如果您检索以下列表Owner您也从所有这些所有者那里检索所有书籍,并且Book实体也可能正在加载它所依赖的其他对象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JPA。如何返回 null 而不是 LazyInitializationException 的相关文章

随机推荐