spring mvc (HATEOS) - jackson json 序列化错误

spring mvc (HATEOS) - jackson json serialize error

这两天我一直在尝试解决我的项目中的 json 序列化问题,我有一堆实体 类 通过一对多关系相互映射。所以错误是

    org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.softcons.cas.entities.CASWeightageMaster.casQuestionMaster, could not initialize proxy - no Session (through reference chain: com.softcons.cas.rest.CASQuestionMasterResource["casQuestionDescriptions"]->org.hibernate.collection.internal.PersistentBag[0]->com.softcons.cas.entities.CASQuestionDescription["casQuestionMaster"]->com.softcons.cas.entities.CASQuestionMaster["casWeightageMaster"]->com.softcons.cas.entities.CASWeightageMaster["casQuestionMaster"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.softcons.cas.entities.CASWeightageMaster.casQuestionMaster, could not initialize proxy - no Session (through reference chain: com.softcons.cas.rest.CASQuestionMasterResource["casQuestionDescriptions"]->org.hibernate.collection.internal.PersistentBag[0]->com.softcons.cas.entities.CASQuestionDescription["casQuestionMaster"]->com.softcons.cas.entities.CASQuestionMaster["casWeightageMaster"]->com.softcons.cas.entities.CASWeightageMaster["casQuestionMaster"])
    org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.writeInternal(MappingJackson2HttpMessageConverter.java:244)
    org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:207)
    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:148)
    org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:125)
    org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:168)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)

QuestionMaster 和 QuestionDescription 之间存在一对多关系,QuestionMaster 和 Weightage 主实体之间也存在多对一关系。我需要获取 QuestionMaster 的单个记录,其中嵌入了 QuestionDescription 记录列表。为此,我使 FetchType 在 QuestionMaster 和 QuestionDescription 之间变得急切,在我引入此 List<QuestionDescription> 作为 json 转换的资源之前,所有这些都用于正常工作。我不断收到上述错误。不知道在哪里修改,因为我尝试了所有可以在网上找到的东西。

question_master

    @Entity
    @Table(name = "cas_question_master")
    public class CASQuestionMaster extends BasicEntity {

    private String question;
    private boolean self;
    private boolean peer;
    private boolean supervisor;
    private boolean subordinate;
    private boolean deleteFlag;
    private boolean inactive;
    private Long deletedBy;
    private Date deletedOn;
    private CASWeightageMaster casWeightageMaster;
    private String questionDef;
    private List<CASQuestionDescription> casQuestionDescription;

    @OneToMany(targetEntity = CASQuestionDescription.class, mappedBy = "casQuestionMaster", cascade = CascadeType.ALL, fetch = FetchType.EAGER) 
    public List<CASQuestionDescription> getCasQuestionDescription() {
        return casQuestionDescription;
    }

    public void setCasQuestionDescription(
            List<CASQuestionDescription> casQuestionDescription) {
        this.casQuestionDescription = casQuestionDescription;
    }

    @Column(name = "question", length = 2000)
    public String getQuestion() {
        return question;
    }

    public void setQuestion(String question) {
        this.question = question;
    }

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumn(name = "weightage_id_fk", nullable = false) 
    public CASWeightageMaster getCasWeightageMaster() {
        return casWeightageMaster;
    }

    public void setCasWeightageMaster(CASWeightageMaster casWeightageMaster) {
        this.casWeightageMaster = casWeightageMaster;
    }
    }

question_description

@Entity
@Table(name = "cas_question_description")
public class CASQuestionDescription extends BasicEntity {

private CASQuestionMaster casQuestionMaster;
private String Description;
private boolean deleteFlag;
private Long deletedBy;
private Date deletedOn;

@ManyToOne
@JoinColumn(name="question_id_fk", nullable=false)
public CASQuestionMaster getCasQuestionMaster() {
    return casQuestionMaster;
}
public void setCasQuestionMaster(CASQuestionMaster casQuestionMaster) {
    this.casQuestionMaster = casQuestionMaster;
}

@Column(name="description", nullable=false)
public String getDescription() {
    return Description;
}
public void setDescription(String description) {
    Description = description;
}

}

weightage_master

    @Entity
    @Table(name = "cas_weightage_master")
    public class CASWeightageMaster extends BasicEntity {
    private int weightage;
    private List<CASQuestionMaster> casQuestionMaster;

    @Column(name = "weightage", nullable=false)
    public int getWeightage() {
        return weightage;
    }

    public void setWeightage(int weightage) {
        this.weightage = weightage;
    }

    @OneToMany(targetEntity = CASQuestionMaster.class, mappedBy = "casWeightageMaster", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    public List<CASQuestionMaster> getCasQuestionMaster() {
        return casQuestionMaster;
    }

    public void setCasQuestionMaster(List<CASQuestionMaster> casQuestionMaster) {
        this.casQuestionMaster = casQuestionMaster;
    }

    }

控制器代码

    @RequestMapping(value="/findAll", method=RequestMethod.GET)
    public ResponseEntity<CASQuestionMasterListResource> findAll()
    {
        try {
            CASQuestionMasterList casQuestionMasterList = new CASQuestionMasterList();            
            casQuestionMasterList.setCasQuestionMasterList(casQuestionMasterService.findAll());             
            CASQuestionMasterListResource res = new CASQuestionMasterListAsm().toResource(casQuestionMasterList);
            return new ResponseEntity<CASQuestionMasterListResource>(res, HttpStatus.OK);
        } catch(Exception exception)
        {
            throw new NotFoundException(exception);
        }
    }

我已将 Spring HATEOS 用于 json 格式。以下是用于转换为 json 的资源文件。以下是json转换

使用的资源文件

问题大师资源

public class CASQuestionMasterResource extends ResourceSupport {

private Long questionId;
private String question;
private Long casWeightageMasterId;

private List<CASQuestionDescription> casQuestionDescriptions;

public List<CASQuestionDescription> getCasQuestionDescriptions() {
    return casQuestionDescriptions;
}
public void setCasQuestionDescriptions(
        List<CASQuestionDescription> casQuestionDescriptions) {
    this.casQuestionDescriptions = casQuestionDescriptions;
}
public String getQuestion() {
    return question;
}
public Long getQuestionId() {
    return questionId;
}
public void setQuestionId(Long questionId) {
    this.questionId = questionId;
}
public void setQuestion(String question) {
    this.question = question;
}

public Long getCasWeightageMasterId() {
    return casWeightageMasterId;
}
public void setCasWeightageMasterId(Long casWeightageMaster) {
    this.casWeightageMasterId = casWeightageMaster;
}


public CASQuestionMaster toQuestionMaster(CASWeightageMaster casWeightageMaster){

    CASQuestionMaster casQuestionMaster = new CASQuestionMaster();

    casQuestionMaster.setId(this.questionId);
    casQuestionMaster.setCasWeightageMaster(casWeightageMaster);
    casQuestionMaster.setQuestion(this.question);

    return casQuestionMaster;
}

}

问题大师列表资源

    public class CASQuestionMasterListResource extends ResourceSupport {
    private List<CASQuestionMasterResource> casQuestionMasterResources = new ArrayList<CASQuestionMasterResource>();

    public List<CASQuestionMasterResource> getCasQuestionMasterResources() {
        return casQuestionMasterResources;
    }

    public void setCasQuestionMasterResources(
            List<CASQuestionMasterResource> casQuestionMasterResources) {
        this.casQuestionMasterResources = casQuestionMasterResources;
    }

    }

QuestionMasterList 汇编程序

    public class CASQuestionMasterListAsm extends ResourceAssemblerSupport<CASQuestionMasterList, CASQuestionMasterListResource>{

    public CASQuestionMasterListAsm() {
        super(CASQuestionMasterController.class, CASQuestionMasterListResource.class);
    }   

    @Override
    public CASQuestionMasterListResource toResource(CASQuestionMasterList casQuestionMasterList) {
        List<CASQuestionMasterResource> casQuestionMasterResources = new CASQuestionMasterAsm().toResources(casQuestionMasterList.getCasQuestionMasterList());
        CASQuestionMasterListResource casQuestionMasterListResource = new CASQuestionMasterListResource();
        casQuestionMasterListResource.setCasQuestionMasterResources(casQuestionMasterResources);
        return casQuestionMasterListResource;       
    }
    }

记录时的最后注意事项是检查问题主实体的记录是否被急切地提取,它会正确记录。真的不知道我要去哪里错了。

序列化到 JSON 可以触发集合的获取,并且由于此时您没有休眠会话,所以会出现异常。

您可以使用 jackson-datatype-hibernate 控制此行为,文档解释了如何配置它

public class HibernateAwareObjectMapper extends ObjectMapper {

    public HibernateAwareObjectMapper() {
        registerModule(new Hibernate4Module());
    }
}

比注册

 <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- Use the HibernateAware mapper instead of the default -->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="path.to.your.HibernateAwareObjectMapper" />
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

该模块有一个Feature.FORCE_LAZY_LOADING设置,告诉是否应该强制加载对象然后序列化,默认设置为。此设置意味着您不会得到异常,而且该列表也不会包含在 json 响应中。

如果你想在响应中使用列表,你可以在你的代码中初始化它,在你有一个有效会话的地方,比如在服务中,通过调用例如Hibernate.initialize(casQuestionMaster.getCasQuestionDescription()) 甚至更好,只需调用列表中的任何 属性,例如getCasQuestionDescription().size(),或将 FORCE_LAZY_LOADING 设置为 true,但这将尝试加载所有延迟加载的集合