Spring JPA懒加载 - 无法初始化代理
Spring JPA懒加载 - 无法初始化代理
以下是一些背景代码:
InitiativeProfileQuestion.java:
@Entity
@Table
public class InitiativeProfileQuestion implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(nullable = false)
private String question;
@Column
private String description;
@Column
private int sortOrder;
@OneToMany(mappedBy = "initiativeProfileQuestion", fetch = FetchType.LAZY)
private List
public List
return answers;
}
public void setAnswers(List
this.answers = answers;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getSortOrder() {
return sortOrder;
}
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
}
InitiativeProfileAnswer.java:
@Entity
@Table
public class InitiativeProfileAnswer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column
private String answer;
@Column
private int sortOrder;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "initiativeProfileQuestionId")
@JsonIgnore
private InitiativeProfileQuestion initiativeProfileQuestion;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public int getSortOrder() {
return sortOrder;
}
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
public InitiativeProfileQuestion getInitiativeProfileQuestion() {
return initiativeProfileQuestion;
}
public void setInitiativeProfileQuestion(InitiativeProfileQuestion initiativeProfileQuestion) {
this.initiativeProfileQuestion = initiativeProfileQuestion;
}
}
InitiativeProfileQuestionRepository.java:
public interface InitiativeProfileQuestionRepository extends JpaRepository
@Query("select ipa from InitiativeProfileQuestion ipa join fetch ipa.answers")
public List
}
InitiativeProfileService.java:
@Service
public class InitiativeProfileService {
@Autowired
private InitiativeProfileQuestionRepository initiativeProfileQuestionRepository;
public List
return initiativeProfileQuestionRepository.findAll();
}
public List
return initiativeProfileQuestionRepository.getAllQuestions();
}
}
BaseController.java:
@RestController
@RequestMapping("/api")
public class BaseController {
@Autowired
InitiativeProfileService initiativeProfileService;
@RequestMapping("/question")
public List
return initiativeProfileService.getAllQuestions();
}
@RequestMapping("/questionFetch")
public List
return initiativeProfileService.getAllQuestionsFetch();
}
}
在我的BaseController中调用getQuestions()会返回一个"could not initialize proxy - no Session"错误。然而,在我的BaseController中调用getQuestionsFetch()可以正常加载。
我希望它能够这样工作:如果我调用getQuestions(),将返回一个没有答案的对象(因为懒加载的对象将不会在任何地方调用)。然而,它只是给我一个错误。如果我使用join fetch进行查询,它将显示答案(预期行为)。
我做错了什么?我尝试在不同的位置使用@Transactional,但没有成功。我也没有.xml文件-到目前为止,一切都是使用注释完成的。
我得到的错误是:
exception
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: failed to lazily initialize a collection of role: com.testApp.domain.InitiativeProfileQuestion.answers, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.testApp.domain.InitiativeProfileQuestion["answers"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.testApp.domain.InitiativeProfileQuestion.answers, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.testApp.domain.InitiativeProfileQuestion["answers"])
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:238)
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:161)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:185)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
root cause
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.testApp.domain.InitiativeProfileQuestion.answers, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.testApp.domain.InitiativeProfileQuestion["answers"])
com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:187)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:647)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase._serializeWithObjectId(BeanSerializerBase.java:558)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:145)
com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:100)
com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:21)
com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:183)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:128)
com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:1902)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231)
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:161)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:185)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
Spring JPA的延迟加载问题是指在将JPA实体对象序列化为JSON时,出现无法初始化代理的错误。根据堆栈跟踪信息,可以看出这个错误是在Jackson尝试访问延迟加载的对象时发生的。而此时,Spring事务已经完成,Hibernate会话已经关闭,因此Hibernate无法再加载该对象。
解决这个问题的方法有两种。首先,如果你不希望该字段被序列化,你可以使用Jackson的JsonIgnore注解,或者使用Spring的Jackson序列化视图支持。
使用JsonIgnore注解的方法如下:
@Entity public class SomeEntity { @JsonIgnore @OneToOne(fetch = FetchType.LAZY) private AnotherEntity anotherEntity; // other fields and methods }
使用Jackson序列化视图支持的方法如下:
首先,在实体类中定义视图接口:
public interface Views { public interface Public {} }
然后,在需要延迟加载的字段上使用@JsonView注解:
@Entity public class SomeEntity { @JsonView(Views.Public.class) @OneToOne(fetch = FetchType.LAZY) private AnotherEntity anotherEntity; // other fields and methods }
最后,在需要进行序列化的地方使用视图接口:
@RestController public class SomeController { @Autowired private SomeEntityRepository someEntityRepository; @JsonView(Views.Public.class) @GetMapping("/entities") public ListgetEntities() { return someEntityRepository.findAll(); } }
通过使用上述方法,可以避免Spring JPA延迟加载导致的无法初始化代理的问题,同时正确地将实体对象序列化为JSON。
Spring JPA 懒加载 - 无法初始化代理
当出现懒加载异常时,你可能会尝试在会话之外获取关联对象。要么将懒加载更改为立即加载,要么将获取关联对象的代码放在会话内部。
例如:
public void test() { Session session = HibernateUtil.currentSession(); session.beginTransaction(); Vehicle vehicle = (Vehicle) session.get(Vehicle.class, 2); System.out.println(vehicle.getVehicleName()); session.getTransaction().commit(); session.close(); System.out.println(vehicle.getVehicleName()); // 这里不会抛出异常 System.out.println(vehicle.getUser().getUserName()); // 这里会抛出异常,将加载方式更改为立即加载或将此行代码放在会话之前,在session.close()之前或session.getTransaction().commit()之前。 }
如果我没有弄错,OP希望避免立即加载。
有两个选择。要么使用立即加载,要么在会话内部获取关联对象。
user3360241是正确的。我希望避免立即加载。有时我想获取InitiativeProfileQuestions对象而不包含答案,因此我认为懒加载是最好的方式。
从我看来,答案只显示了立即加载选项。你的最后一行注释表示要避免异常,需要立即获取用户。
我已经提供了一个例子,可能是你代码中最有可能的原因,并提供了两种修复方法。其他选项包括:使用查询获取或使用FetchMode.EAGER(如果使用criteria)。
使用查询获取可以工作,但是如果查询不获取答案,当应该只返回问题对象而没有答案时,会抛出无会话错误。
你可以分享一下使用会话获取它的代码吗?
你肯定会得到"Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session"的错误。
无法写入内容:无法懒加载角色的集合:com.testApp.domain.InitiativeProfileQuestion.answers,无法初始化代理 - 没有会话(通过引用链:java.util.ArrayList[0] -> com.testApp.domain.InitiativeProfileQuestion["answers"]);嵌套异常是com.fasterxml.jackson.databind.JsonMappingException: 无法懒加载角色的集合:com.testApp.domain.InitiativeProfileQuestion.answers,无法初始化代理 - 没有会话(通过引用链:java.util.ArrayList[0] -> com.testApp.domain.InitiativeProfileQuestion
只有在没有使用join fetch answers的查询时,才会出现上述错误。如果我不明确告诉它要join fetch answers,它不应该只返回一个空的answers对象吗?而不是抛出那个错误。
我已经对最后一行进行了更改。请尝试按照最后一行的建议进行更改。