Spring MVC、Hibernate:惰性初始化异常

Spring MVC, Hibernate : Lazy initialization exception

我正在开发一个 Spring-MVC 应用程序,其中有 3 个 classes、GroupCanvas、GroupSection、GroupNotes。 GroupCanvas 与 GroupSection 一对多映射,GroupSection 与 GroupNotes 一对多映射。我正在尝试根据 GroupCanvas 的主键检索笔记,但我收到了一个 Hibernate 惰性初始化异常。我尝试了网上的建议,主要是 SO,但其中 none 似乎有帮助。这是代码。

DAO 方法抛出错误:

 @Override
    public List<GroupNotes> searchNotesByDays(int days, int mcanvasid) {
        Session session = this.sessionFactory.getCurrentSession();
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, -days);
        long daysAgo = cal.getTimeInMillis();
        Timestamp nowMinusDaysAsTimestamp = new Timestamp(daysAgo);
        Query query = session.createQuery("from GroupSection as n where n.currentcanvas.mcanvasid=:mcanvasid");
        query.setParameter("mcanvasid", mcanvasid);
        List<GroupSection> sectionList = query.list();
        List<GroupNotes> notesList = new ArrayList<GroupNotes>();
        for (GroupSection e : sectionList) {
            Query query1 = session.createQuery("from GroupNotes as n where n.ownednotes.msectionid=:msectionid and n.noteCreationTime >:limit");
            query1.setParameter("limit", nowMinusDaysAsTimestamp);
            query1.setParameter("msectionid",e.getMsectionid());
            notesList.addAll(query1.list());
        }
        return notesList;
    }

GroupCanvas 模型:

@Entity
@Table(name = "membercanvas")
public class GroupCanvas{

variables, getters, setters ignored
    @OneToMany(mappedBy = "currentcanvas",fetch=FetchType.LAZY, cascade = CascadeType.REMOVE)
    @JsonIgnore
    private Set<GroupSection> ownedsection = new HashSet<>();

    public Set<GroupSection> getOwnedsection() {
        return this.ownedsection;
    }

    public void setOwnedsection(Set<GroupSection> ownedsection) {
        this.ownedsection = ownedsection;
    }
}

GroupSection 模型class:

@Entity
@Table(name = "membersection")
public class GroupSection {
 @ManyToOne
    @JoinColumn(name = "groupcanvasid",nullable = false)
    @JsonIgnore
    private GroupCanvas currentcanvas;

    public GroupCanvas getCurrentcanvas() {
        return this.currentcanvas;
    }

    public void setCurrentcanvas(GroupCanvas currentcanvas) {
        this.currentcanvas = currentcanvas;
    }

    public int getCurrentCanvasId(){
        return this.currentcanvas.getMcanvasid();
    }


    @OneToMany(mappedBy = "ownednotes", fetch = FetchType.EAGER,cascade = CascadeType.REMOVE)
    @JsonIgnore
    private Set<GroupNotes> sectionsnotes = new HashSet<>();

    public Set<GroupNotes> getSectionsnotes(){
        return this.sectionsnotes;
    }

    public void setSectionsnotes(Set<GroupNotes> sectionsnotes){
        this.sectionsnotes=sectionsnotes;
    }
}

群组笔记:

@Entity
@Table(name="groupnotes")
public class GroupNotes{
   @ManyToOne
    @JoinColumn(name = "msectionid")
    @JsonIgnore
    private GroupSection ownednotes;

    public GroupSection getOwnednotes(){return this.ownednotes;}

    public void setOwnednotes(GroupSection ownednotes){this.ownednotes=ownednotes;}
}

错误日志:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.journaldev.spring.model.GroupCanvas.ownedsection, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.journaldev.spring.model.GroupNotes["ownednotes"]->com.journaldev.spring.model.GroupSection["currentcanvas"]->com.journaldev.spring.model.GroupCanvas["ownedsection"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.journaldev.spring.model.GroupCanvas.ownedsection, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.journaldev.spring.model.GroupNotes["ownednotes"]->com.journaldev.spring.model.GroupSection["currentcanvas"]->com.journaldev.spring.model.GroupCanvas["ownedsection"])
    org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.writeInternal(MappingJackson2HttpMessageConverter.java:256)

我做错了什么,请告诉我。如果需要更多信息,请发表评论。

您的 JSON 转换器在 Hibernate 会话完成后执行。 JSON 转换器盲目地访问所有的 getter 和 setter,甚至是懒惰的。因此,当 Hibernate 尝试初始化 GroupCanvas#ownedSection 时,没有可用的会话,因此会抛出此异常。

可能的解决方案:

  1. 不要直接在 Hibernate 管理的对象上执行 JSON 转换器。创建 DTO 对象来完成这项工作。 DTO 对象没有逻辑,是纯粹的 java bean,非常适合这个角色。但缺点是您必须维护另一个 class 层次结构。好处确实大于缺点。以下 post 可以帮助实现此方法:

    DTO pattern : Best way to copy properties between two Objects

  2. 使用注释将某些字段标记为不可序列化。例如,JsonIgnore。这样做的缺点是,如果在不同的 API 中需要此字段,则您不能使用它。

  3. 如果可以从您的模型中删除其中一个反向引用(注释->section/section->canvas),那么它就是 "friendlier"到序列化。在其他作品中,JSON 不能很好地与循环引用一起使用,因此 bi-directional/loop 构造的数量越少越好。如果不是因为循环引用的可能性,那么你可以初始化所有序列化所需的数据,包括GroupCanvas。