休眠 + Lombok 的 Id 字段的 LazyInitializationException
LazyInitializationException for Id field with hibernate + Lombok
当我将 Lombok 项目添加到我的休眠项目中并在实体 class 上使用它的 @Getter
和 @Setter
时,我遇到了延迟初始化问题。
实体 classes 注释为 Javax.persistence 的 @Entity
,因为我使用的是 hibernate 5.
发出堆栈跟踪 :-
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
at com.capehenry.domain.user.User_$$_jvst52e_9.getId(User_$$_jvst52e_9.java)
at com.capehenry.business.rs.course.SeatRequestResource.validateSeatRequestCancel(SeatRequestResource.java:338)
at com.capehenry.business.rs.course.SeatRequestResource.cancel(SeatRequestResource.java:220)
使用以下代码一切正常
@Entity
@Audited
@Table(name = "seat_request")
public class SeatRequest extends BaseEntity {
private CourseSchedule courseSchedule;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "courseScheduleId", nullable = false)
public CourseSchedule getCourseSchedule() {
return courseSchedule;
}
public void setCourseSchedule(CourseSchedule courseSchedule) {
this.courseSchedule = courseSchedule;
}
当我执行 searRequest.getCourseSchedule().getId() 时,它在 rest 层工作意味着在事务之外。
一旦我将代码更改为下面(添加 lombok),rest 层的 searRequest.getCourseSchedule().getId() 就会开始抛出 lazyInitializationException :-
@Entity
@Audited
@Table(name = "seat_request")
@Setter
public class SeatRequest extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY, optional=false)
@JoinColumn(name = "courseScheduleId", nullable = false)
private CourseSchedule courseSchedule;
注意:-
1) 我必须强制使用 Lombok 项目
2) 我必须在 Sevrice 和 trasaction
之外使用 searRequest.getCourseSchedule().getId()
请提出解决方案,提前致谢!!
I have to use searRequest.getCourseSchedule().getId() outside Service and transaction
我刚才注意到了这一点...如果您在服务和交易之外,您将始终遇到该异常。尝试使用 FetchType.EAGER
,它应该可以工作。
当您退出事务时,您的实体将被分离,这意味着您标记为惰性的所有集合都不会被加载。所以你有两个选择:第一个是在事务内执行对集合 getter 的所有调用,第二个是将集合标记为急切,这样当 Hibernate 加载实体时,它也会立即加载引用的集合。或者,您可以在交易中将您的实体映射到 DTO。只要您在事务中,延迟加载字段的 getter 将始终有效,因此 DTO 的映射器将访问所有信息。一旦 DTO 脱离事务,您将可以访问您映射的所有字段,然后做任何您想做的事。
这是我最终解决问题的方法!
我认为问题是在与 Lombok 项目集成后开始的,但是当注释从方法 (属性) 级别移动到字段级别时问题就开始了。
请耐心等待我的长答案。
这里foreign指的是数据库级别的foreign tables.
要从外部 table 的 外部事务 访问任何列,您需要使用 FetchType.Eager (这是任何外部对象在休眠中的默认设置)或需要join/subquery 即 table。
但是,如果您只想获取连接了 2 个 table 的外键(列)(在我们的例子中是 ID)并希望保留 FetchType.LAZY 那么你可以通过两种方式进行:-
1) 在 getter 方法上保留注释(manyToOne、JoinColumn 等)
2) 如果必须在 字段级别 保留注释,则在父 table 的外键字段上再写一个注释,即 - @Access(AccessType.PROPERTY)
所以在上面的代码中解决我当然在 id 字段上添加了这个注释 Schedule
@Entity
@Audited
@Table(name = "course_schedule")
@Getter
@Setter
public class CourseSchedule{
@Id
@GenericGenerator(name = "autoincr", strategy = "native")
@GeneratedValue(generator = "autoincr")
@Column(name = "id", unique = true, nullable = false)
@Access(AccessType.PROPERTY)
protected Long id;
..........
}
所以座位请求中不需要更改。
当我将 Lombok 项目添加到我的休眠项目中并在实体 class 上使用它的 @Getter
和 @Setter
时,我遇到了延迟初始化问题。
实体 classes 注释为 Javax.persistence 的 @Entity
,因为我使用的是 hibernate 5.
发出堆栈跟踪 :-
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
at com.capehenry.domain.user.User_$$_jvst52e_9.getId(User_$$_jvst52e_9.java)
at com.capehenry.business.rs.course.SeatRequestResource.validateSeatRequestCancel(SeatRequestResource.java:338)
at com.capehenry.business.rs.course.SeatRequestResource.cancel(SeatRequestResource.java:220)
使用以下代码一切正常
@Entity
@Audited
@Table(name = "seat_request")
public class SeatRequest extends BaseEntity {
private CourseSchedule courseSchedule;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "courseScheduleId", nullable = false)
public CourseSchedule getCourseSchedule() {
return courseSchedule;
}
public void setCourseSchedule(CourseSchedule courseSchedule) {
this.courseSchedule = courseSchedule;
}
当我执行 searRequest.getCourseSchedule().getId() 时,它在 rest 层工作意味着在事务之外。
一旦我将代码更改为下面(添加 lombok),rest 层的 searRequest.getCourseSchedule().getId() 就会开始抛出 lazyInitializationException :-
@Entity
@Audited
@Table(name = "seat_request")
@Setter
public class SeatRequest extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY, optional=false)
@JoinColumn(name = "courseScheduleId", nullable = false)
private CourseSchedule courseSchedule;
注意:-
1) 我必须强制使用 Lombok 项目
2) 我必须在 Sevrice 和 trasaction
请提出解决方案,提前致谢!!
I have to use searRequest.getCourseSchedule().getId() outside Service and transaction
我刚才注意到了这一点...如果您在服务和交易之外,您将始终遇到该异常。尝试使用 FetchType.EAGER
,它应该可以工作。
当您退出事务时,您的实体将被分离,这意味着您标记为惰性的所有集合都不会被加载。所以你有两个选择:第一个是在事务内执行对集合 getter 的所有调用,第二个是将集合标记为急切,这样当 Hibernate 加载实体时,它也会立即加载引用的集合。或者,您可以在交易中将您的实体映射到 DTO。只要您在事务中,延迟加载字段的 getter 将始终有效,因此 DTO 的映射器将访问所有信息。一旦 DTO 脱离事务,您将可以访问您映射的所有字段,然后做任何您想做的事。
这是我最终解决问题的方法!
我认为问题是在与 Lombok 项目集成后开始的,但是当注释从方法 (属性) 级别移动到字段级别时问题就开始了。
请耐心等待我的长答案。
这里foreign指的是数据库级别的foreign tables.
要从外部 table 的 外部事务 访问任何列,您需要使用 FetchType.Eager (这是任何外部对象在休眠中的默认设置)或需要join/subquery 即 table。
但是,如果您只想获取连接了 2 个 table 的外键(列)(在我们的例子中是 ID)并希望保留 FetchType.LAZY 那么你可以通过两种方式进行:-
1) 在 getter 方法上保留注释(manyToOne、JoinColumn 等)
2) 如果必须在 字段级别 保留注释,则在父 table 的外键字段上再写一个注释,即 - @Access(AccessType.PROPERTY) 所以在上面的代码中解决我当然在 id 字段上添加了这个注释 Schedule
@Entity
@Audited
@Table(name = "course_schedule")
@Getter
@Setter
public class CourseSchedule{
@Id
@GenericGenerator(name = "autoincr", strategy = "native")
@GeneratedValue(generator = "autoincr")
@Column(name = "id", unique = true, nullable = false)
@Access(AccessType.PROPERTY)
protected Long id;
..........
}
所以座位请求中不需要更改。