英文:
LazyInitializationException: load lazy attribute from a session different from the original one in Hibernate
问题
我是初学者,正在学习 Hibernate,并尝试了解急加载和懒加载。
我知道如果 Hibernate 会话已关闭,然后我尝试检索懒加载数据,那么 Hibernate 将抛出异常。
现在,我尝试在第一个会话中加载主实体(Instructor),然后在一个***新的独立会话***中加载从属实体(Course):
**主类(测试类)**
```java
public class EagerLazyDemo {
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml")
.addAnnotatedClass(Instructor.class)
.addAnnotatedClass(InstructorDetail.class)
.addAnnotatedClass(Course.class)
.buildSessionFactory();
Session session = factory.getCurrentSession();
// 开始事务
session.beginTransaction();
// 从数据库获取讲师信息
Instructor theInstructor = session.get(Instructor.class, 1);
// 提交事务
session.getTransaction().commit();
session.close();
//------
// 新会话
session = factory.getCurrentSession();
session.beginTransaction();
// 获取并显示讲师的课程
List<Course> courses = theInstructor.getCourses();
printCourses(courses);
session.getTransaction().commit();
session.close();
}
private static void printCourses(List<Course> courses) {
for (Course c : courses) {
System.out.println(c);
}
}
}
但是 Hibernate 抛出了以下异常:
错误:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mehdisarf.hibernate.demo.entity.Instructor.courses, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.printCourses(EagerLazyDemo.java:55)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.main(EagerLazyDemo.java:42)
它表示:
> 无法初始化代理 - 没有会话
尽管我已经为加载从属实体创建了一个会话
以下是我的实体类:
讲师类(主实体)
@Entity
@Table(name = "instructor")
public class Instructor {
// ... (省略其他代码)
@OneToMany(fetch = FetchType.LAZY,
mappedBy = "instructor",
cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<Course> courses;
// ... (省略其他代码)
}
课程类(从属实体)
@Entity
@Table(name = "course")
public class Course {
// ... (省略其他代码)
@ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name = "instructor_id")
private Instructor instructor;
// ... (省略其他代码)
}
<details>
<summary>英文:</summary>
I'm beginner at hibernate and try to learn eager and lazy loading
I know that if the hibernate session is closed and then I try to retrieve lazy data, then Hibernate will throw an exception.
Now, I've tried to load main entity(Instructor) in first session and then load dependent entity(Course) in a ***new separate session***:
**Main Class(Test Class)**
public class EagerLazyDemo {
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml")
.addAnnotatedClass(Instructor.class)
.addAnnotatedClass(InstructorDetail.class)
.addAnnotatedClass(Course.class)
.buildSessionFactory();
Session session = factory.getCurrentSession();
// Begin A Transaction
session.beginTransaction();
// Get The Instructor From DB
Instructor theInstructor = session.get(Instructor.class, 1);
// Commit Transaction
session.getTransaction().commit();
session.close();
//------
//new session
session = factory.getCurrentSession();
session.beginTransaction();
//Get And Display Courses For The Instructor
List<Course> courses = theInstructor.getCourses();
printCourses(courses);
session.getTransaction().commit();
session.close();
}
private static void printCourses(List<Course> courses) {
for (Course c : courses) {
System.out.println(c);
}
}
}
but hibernate throw this exception:
**Error:**
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mehdisarf.hibernate.demo.entity.Instructor.courses, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.printCourses(EagerLazyDemo.java:55)
at com.mehdisarf.hibernate.demo.EagerLazyDemo.main(EagerLazyDemo.java:42)
**it says:**
> could not initialize proxy - no Session
Although I have a session for loading dependent entity
**These Are My Entity Classes:**
**Instructor Class (main entity)**
@Entity
@Table(name = "instructor")
public class Instructor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "instructor_detail_id")
private InstructorDetail instructorDetail;
@OneToMany(fetch = FetchType.LAZY,
mappedBy = "instructor",
cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<Course> courses;
public Instructor() {
}
public Instructor(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public void addCourse(Course theCourse) {
if (courses == null) {
courses = new ArrayList<>();
}
this.courses.add(theCourse);
theCourse.setInstructor(this);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public InstructorDetail getInstructorDetail() {
return instructorDetail;
}
public void setInstructorDetail(InstructorDetail instructorDetail) {
this.instructorDetail = instructorDetail;
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
@Override
public String toString() {
return id + "" + firstName + "" + lastName + "" + email + " (Detail:" + instructorDetail+")";
}
}
**Course Class (dependent entity)**
@Entity
@Table(name = "course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "title")
private String title;
@ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name = "instructor_id")
private Instructor instructor;
public Course() {
}
public Course(String title) {
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Instructor getInstructor() {
return instructor;
}
public void setInstructor(Instructor instructor) {
this.instructor = instructor;
}
@Override
public String toString() {
return id + "|" + title + "|";
}
}
</details>
# 答案1
**得分**: 2
你的对象theInstructor仍然与之前的会话关联。为了检索你的对象,在打开新会话后,你需要进行另一个调用:
theInstructor = session.get(Instructor.class, 1);
<details>
<summary>英文:</summary>
Your object theInstructor is still linked to the former session. In order to retrieve your object you have to make another call after opening the new session :
theInstructor = session.get(Instructor.class, 1);
</details>
# 答案2
**得分**: 1
"theInstructor"在进行"theInstructor.getCourses()"调用时,并未连接到任何会话,这导致了异常。
当对象通过get方法返回时,它处于持久状态。
> 任何由get()或load()方法返回的实例都是持久的。[会话文档][1]
当会话关闭时,"theInstructor"对象进入脱离状态。
> 脱离状态 - 脱离的实例是指已经持久化,但其会话已关闭的对象。[Hibernate对象状态文档][2]
调用`session = factory.getCurrentSession();`返回新的会话对象,但"theInstructor"对象并未连接到新会话。因此,尝试惰性加载课程时会失败。
[1]: https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#get(java.lang.Class,%20java.io.Serializable)
[2]: https://docs.jboss.org/hibernate/core/3.5/reference/en/html/objectstate.html
<details>
<summary>英文:</summary>
"theInstructor" is not attached to any session while "theInstructor.getCourses()" call is made, this results in exception.
When object is returned by the get method it is in the Persistent state.
> Any instance returned by a get() or load() method is persistent
[Session Documentation][1]
When session is closed "theInstructor" object goes into the Detached state.
> Detached - a detached instance is an object that has been persistent, but its Session has been closed. [Hibernate object states documentation][2]
Call made to `session = factory.getCurrentSession();` returns new session object, but `theInstructor` object is not attached to the new session. Hence an attempt to lazily load the courses fails.
[1]: https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#get(java.lang.Class,%20java.io.Serializable)
[2]: https://docs.jboss.org/hibernate/core/3.5/reference/en/html/objectstate.html
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论