Spring JPA懒加载 - 无法初始化代理

9 浏览
0 Comments

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 answers;

public List getAnswers() {

return answers;

}

public void setAnswers(List 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 {

@Query("select ipa from InitiativeProfileQuestion ipa join fetch ipa.answers")

public List getAllQuestions();

}

InitiativeProfileService.java:

@Service

public class InitiativeProfileService {

@Autowired

private InitiativeProfileQuestionRepository initiativeProfileQuestionRepository;

public List getAllQuestions() {

return initiativeProfileQuestionRepository.findAll();

}

public List getAllQuestionsFetch() {

return initiativeProfileQuestionRepository.getAllQuestions();

}

}

BaseController.java:

@RestController

@RequestMapping("/api")

public class BaseController {

@Autowired

InitiativeProfileService initiativeProfileService;

@RequestMapping("/question")

public List getQuestions() {

return initiativeProfileService.getAllQuestions();

}

@RequestMapping("/questionFetch")

public List getQuestionsFetch() {

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)

0
0 Comments

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 List getEntities() {
        return someEntityRepository.findAll();
    }
}

通过使用上述方法,可以避免Spring JPA延迟加载导致的无法初始化代理的问题,同时正确地将实体对象序列化为JSON。

0
0 Comments

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对象吗?而不是抛出那个错误。

我已经对最后一行进行了更改。请尝试按照最后一行的建议进行更改。

0