[英]how can to resolve org.hibernate.LazyInitializationException?
[英]Spring and Hibernate web application: how to resolve 'org.hibernate.LazyInitializationException?
我正在使用Spring 4,而Hibernate 4是一个场景,其中我有两个类Person和Book,它们映射到数据库中的两个表。 人与书之间的关系为1:M,即一个人可以拥有许多书。
人员表中的关系定义为:
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="PERSON_ID")
private List<Book> books;
并在书中:
@ManyToOne
private Person person;
使用适当的getter和setter方法。
但是按照我的方法显示一个人的书:
// Calls books.jsp for a Person.
@RequestMapping(value = "/books", method = RequestMethod.GET)
public String listBooks(@RequestParam("id") String id,
Model model) {
logger.info(PersonController.class.getName() + ".listBooks() method called.");
Person person = personService.get(Integer.parseInt(id));
// List<Book> books = bookService.listBooksForPerson(person.getId());
// Set view.
model.addAttribute("person", person);
// model.addAttribute("books", books);
return "view/books";
}
我发现Hibernate返回:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: library.model.Person.books, could not initialize proxy - no Session
所以我有两种选择:
如果此问题的答案与在服务或DAO代码中指定/使用事务有关,通常我的数据库更新将在服务类中标记为@TRANSACTIONAL。 并实现为:
@Override
public void insert(Person person) {
logger.info(PersonDAOImpl.class.getName() + ".insert() method called.");
Session session = sessionFactory.openSession();
Transaction transaction = session.getTransaction();
try {
transaction.begin();
session.save(person);
transaction.commit();
}
catch(RuntimeException e) {
transaction.rollback();
throw e;
}
finally {
session.close();
}
}
我的DAO get方法在其SQL中不使用JOIN,例如:
@Override
public List<Book> listBooksForPerson(Integer id) {
logger.info(BookDAOImpl.class.getName() + ".listBooksForPerson() method called.");
Session session = sessionFactory.openSession();
try {
Query query = session.createQuery("FROM Book WHERE PERSON_ID = " + id);
return query.list();
}
finally {
session.close();
}
}
我是Spring的新手,我已经在网站上阅读了一些有关此问题的问题,并了解了问题的本质,当Hibernate会话关闭时,可以重新EAGER或LAZY获取相关数据。 但是我不知道如何最好地解决上面给出的代码。
有人可以建议吗?
好吧,这样的方法失败了:
@Override
public List<Book> listBooksForPerson(Integer id) {
logger.info(PersonDAOImpl.class.getName() + ".listBooksForPerson() method called.");
Session session = sessionFactory.openSession();
try {
Query query = session.createQuery("FROM Person, Book WHERE Person.ID = " + id + " AND Person.ID = Book.PERSON_ID");
return query.list();
}
finally {
session.close();
}
}
由于Your page request has caused a QuerySyntaxException: Invalid path: 'null.ID' [FROM library.model.Person, library.model.Book WHERE Person.ID = 1 AND Person.ID = Book.PERSON_ID] error:
我也尝试了以下方法:
@Override
public Person getPersonAndBooks(Integer id) {
logger.info(PersonDAOImpl.class.getName() + ".listBooksForPerson() method called.");
Session session = sessionFactory.openSession();
try {
SQLQuery query = session.createSQLQuery("SELECT * FROM Person JOIN Book ON Person.ID = book.PERSON_ID AND Person.ID = " + id);
// HOW TO GET PERSON OBJECT HERE WITH BOOKS?
}
finally {
session.close();
}
}
但是,如何将查询的输出映射到我的Person对象类型,其中Person是:
// Attributes.
@Id
@Column(name = "ID", unique = true, nullable = false)
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
@Column(name = "NAME", nullable = false, length=50)
private String name;
@Column(name = "ADDRESS", nullable = false, length=100)
private String address;
@Column(name = "TELEPHONE", nullable = false, length=10)
private String telephone;
@Column(name = "EMAIL", nullable = false, length=50)
private String email;
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="PERSON_ID")
private List<Book> books;
有人可以建议吗?
通过调整DAO方法以使Person达到以下条件,可以解决此查询:
@Override
public Person get(Integer personId) {
logger.info(PersonDAOImpl.class.getName() + ".get() method called.");
Session session = sessionFactory.openSession();
try {
Query query = session.createQuery(" from Person as p left join fetch p.books where p.personId = :personId").setParameter("personId", personId);
Person person = (Person) query.list().get(0);
Hibernate.initialize(person.getBooks());
return person;
// return (Person) session.get(Person.class, personId);
}
finally {
session.close();
}
}
上面的方法listBooksForPerson
已被删除。
但是可以这么说,我仍然使用表之间关系的一端。 Book表现在不使用@ManyToOne
标记或Person实例。 这是最终使LAZY
获取(显示)工作的唯一方法。
您可能已经知道,问题是一旦将实体发送到视图后,它就会与EntityManager分离,因此延迟加载在那时将不起作用。 如果尝试在listBooks
使用personService.get
,则在加载books
之前,将从EntityManager中分离出检索到的Person
。 因此,当您尝试在视图中访问books
,会收到该错误。
答案是确保在分离实体之前将视图所需的数据加载到实体中。 不要使用personService.get
; 而是使用HQL查询和JOIN FETCH
语法加载所需的惰性字段。 尝试类似:
SELECT p FROM Person p LEFT JOIN FETCH p.books WHERE p.id = ...
在这种情况下,将加载books
,您可以在视图中使用它。
您也可以级联联接提取。 例如,如果Book
包含您需要处理的Chapter
的惰性集合,则可以使用:
SELECT p FROM Person p LEFT JOIN FETCH p.books book LEFT JOIN FETCH book.chapters WHERE p.id = ...
在“ Person
,所有书籍以及这些书籍中的所有章节将对视图可用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.