JPA。如何返回null而不是LazyInitializationException [英] JPA. How to return null instead of LazyInitializationException

查看:203
本文介绍了JPA。如何返回null而不是LazyInitializationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两张一对多关系表。我使用Jpa + Spring JpaRepository。有时我必须从内部对象的数据库中获取对象。有时候我不需要。存储库始终使用内部对象返回对象。
我试图从数据库中获得所有者,并且我总是获得Set书籍;没关系。但是当我读这本内部书籍的领域时,我得到了LazyInitializationException。如何获得null而不是Exception?

  @Entity 
@Table(name =owners)
@NamedEntityGraph(name =Owner.books,
attributeNodes = @NamedAttributeNode(books))
公共类拥有者实现Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name =owner_id,nullable = false,unique = true)
私人长ID;

@Column(name =owner_name,nullable = false)
私有字符串名称;

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

public Worker(){
}
}



@实体
@Table(name = book)
@NamedEntityGraph(name =Book.owner,
attributeNodes = @NamedAttributeNode(owner))
public class Book实现Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name =book_id,unique = true,nullable = false)
private长ID;

@Column(name =book_name,nullable = false,unique = true)
私有字符串名称;


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name =owner_id)
私有者所有者;
$ b $ public Task(){
}
}

公共接口BookRepository扩展JpaRepository< Book,Long> {


@EntityGraph(value =Book.owner,type = EntityGraph.EntityGraphType.LOAD)
List< Book> findAllWithOwner();

@Query(从t选择t,其中t.id =:aLong)
@EntityGraph(value =Book.owner,type = EntityGraph.EntityGraphType.LOAD)
Book findOneWithOwner(Long aLong);


解决方案

$ c> LazyInitializationException 因为您正在访问书籍的内容设置在事务上下文之外,很可能是因为它已经关闭。例如:

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

  public所有者getOwner(Integer id){
所有者owner = ownerRepository.findOne(id);
//您尝试访问Set here
返回所有者;
}

此时您有一个Owner对象, ,并且只有当有人想要访问其内容时才会填充。如果存在未完成交易,则只能填充图书集。不幸的是, findOne 方法已经打开并且已经关闭了事务,所以没有打开的事务,您将得到臭名昭着的 LazyInitializationException 当你做一些像 owner.getBooks()。size()



你有几个选项:

使用@Transactional



正如OndrejM所说,你需要以一种全部执行的方式来包装代码在同一笔交易中。最简单的方法是使用Spring的 @ Transactional 注解:

  @Transactional 
public所有者getOwner(Integer id){
所有者所有者= ownerRepository.findOne(id);
//您可以在这里访问owner.getBooks()内容,因为交易仍然开放
返回所有者;使用fetch = FetchType.EAGER



$ b $ $ b

您在 @Column 定义中有 fetch = FecthType.LAZY ,这就是Set的原因懒惰地加载(这也是JPA默认使用的获取类型,如果没有指定)。如果你希望在你从数据库获得Owner对象之后自动完全填充Set,你应该像这样定义它:

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

如果 Book 实体不是很重并且每个所有者没有大量的书籍,因此将该所有者的所有书籍从数据库中取出并不是犯罪行为。但是你也应该知道,如果你检索所有者的列表,你也检索所有这些所有者的所有书籍,并且 Book 实体可能正在加载它所依赖的其他对象。


I have two tables with 'one to many' relationship. I use Jpa + Spring JpaRepository. Sometimes I have to get object from Database with internal object. Sometimes I dont't have to. Repositories always return object with internal objects. I try to get 'Owner' from Database and I always get Set books; It's OK. But when I read fields of this internal Book , I get LazyInitializationException. How to get null instead of 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);
}

解决方案

You are getting LazyInitializationException because you are accessing the content of the books Set outside the context of a transaction, most likely because it's already closed. Example:

You get an Owner from the database with your DAO or Spring Data repository, in a method in your Service class:

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

At this point you have an Owner object, with a books Set which is empty, and will only be populated when someone wants to access its contents. The books Set can only be populated if there is an open transaction. Unfortunately, the findOne method has opened and already closed the transaction, so there's no open transaction and you will get the infamous LazyInitializationException when you do something like owner.getBooks().size().

You have a couple of options:

Use @Transactional

As OndrejM said you need to wrap the code in a way that it all executes in the same transaction. And the easiest way to do it is using Spring's @Transactional annotation:

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

Use fetch = FetchType.EAGER

You have fetch = FecthType.LAZY in you @Column definition and that's why the Set is being loaded lazily (this is also the fetch type that JPA uses by default if none is specified). If you want the Set to be fully populated automatically right after you get the Owner object from the database you should define it like this:

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

If the Book entity is not very heavy and every Owner does not have a huge amount of books it's not a crime to bring all the books from that owner from the database. But you should also be aware that if you retrieve a list of Owner you are retrieving all the books from all those owners too, and that the Book entity might be loading other objects it depends on as well.

这篇关于JPA。如何返回null而不是LazyInitializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆