Spring JPA 延迟加载 - 无法初始化代理
Posted
技术标签:
【中文标题】Spring JPA 延迟加载 - 无法初始化代理【英文标题】:Spring JPA Lazy Loading - Could Not Initialize Proxy 【发布时间】:2015-11-01 17:24:21 【问题描述】:下面是一些背景代码:
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<InitiativeProfileAnswer> answers;
public List<InitiativeProfileAnswer> getAnswers()
return answers;
public void setAnswers(List<InitiativeProfileAnswer> answers)
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<InitiativeProfileQuestion, Long>
@Query("select ipa from InitiativeProfileQuestion ipa join fetch ipa.answers")
public List<InitiativeProfileQuestion> getAllQuestions();
InitiativeProfileService.java:
@Service
public class InitiativeProfileService
@Autowired
private InitiativeProfileQuestionRepository initiativeProfileQuestionRepository;
public List<InitiativeProfileQuestion> getAllQuestions()
return initiativeProfileQuestionRepository.findAll();
public List<InitiativeProfileQuestion> getAllQuestionsFetch()
return initiativeProfileQuestionRepository.getAllQuestions();
BaseController.java:
@RestController
@RequestMapping("/api")
public class BaseController
@Autowired
InitiativeProfileService initiativeProfileService;
@RequestMapping("/question")
public List<InitiativeProfileQuestion> getQuestions()
return initiativeProfileService.getAllQuestions();
@RequestMapping("/questionFetch")
public List<InitiativeProfileQuestion> getQuestionsFetch()
return initiativeProfileService.getAllQuestionsFetch();
在我的 BaseController 中调用 getQuestions() 会返回“无法初始化代理 - 无会话”错误。但是,在我的 BaseController 中调用 getQuestionsFetch() 加载就好了。
我希望它以某种方式工作,如果我调用 getQuestions(),对象将返回没有答案(因为不会在任何地方调用延迟加载的对象)。但是,它只是给了我一个错误。如果我正在使用连接提取进行查询,它也会通过显示答案来工作(预期行为)。
我做错了什么?我在不同的地方尝试了@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)
【问题讨论】:
"它只是给我一个错误" ...什么错误?特别是,堆栈跟踪会告诉您谁正在访问“不会在任何地方调用”的延迟加载对象。 无法写入内容:无法延迟初始化角色集合: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 使用问题下方的“编辑”链接将此信息添加到您的答案中(人们通常不会阅读整个评论链,没有长度限制,您可以更好地格式化它) .另外,我要求提供 stacktrace,而不仅仅是异常消息。 以上更新。谢谢 几个小时后,我从这篇文章中弄清楚了:***.com/questions/21708339/… 【参考方案1】:对于延迟加载的例外情况,您必须尝试在会话之外获取关联的对象。要么将延迟加载更改为渴望,要么将获取关联对象的代码放入会话中。 例如:
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()); //No exception here
System.out.println(vehicle.getUser().getUserName());
// Exception here change the loading to EAGER or put this line of code within the session above. Put it before session.close() or before session.getTransaction().commit();
【讨论】:
如果我没记错的话,OP 想避免急切加载。 2 个选项是他们的。使用 Eager 或获取会话中的关联对象。 user3360241 是正确的。我想避免急于加载。有时我想在没有答案的情况下检索 InitiativeProfileQuestions 对象,因此我认为延迟加载是最好的方法。 据我所知,答案只显示了急切的选择。最后一行的注释表示避免异常急切地获取用户。 我提供了一个示例,说明您的代码中最可能的原因以及修复它的 2 个选项。其他选项是:如果使用条件,您可以使用查询来获取或使用 FetchMode.EAGER。【参考方案2】:从堆栈跟踪中可以看出,当 Jackson 尝试访问延迟加载的对象以将其序列化为 JSON 时,就会发生错误。此时spring事务已经完成,Hibernate会话关闭,这就是为什么Hibernate不能再加载那个对象了。
如果您不打算序列化该字段,您可能希望使用@JsonIgnore 或Spring's Jackson Serialization View Support。
【讨论】:
以上是关于Spring JPA 延迟加载 - 无法初始化代理的主要内容,如果未能解决你的问题,请参考以下文章
无法懒惰地初始化角色集合,..无法初始化代理 - 无会话 - JPA + SPRING
Spring Data JPA - 在没有 @Transactional 的情况下获取延迟加载的集合
如何在 Spring Boot JPA 中的 OneToMany 中为孩子获取 Null 值