Spring MVC, Hibernate : Lazy initialization exception

813 views Asked by At

I am working on a Spring-MVC appplication in which I have 3 classes, GroupCanvas, GroupSection, GroupNotes. GroupCanvas has one-to-many mapping with GroupSection and GroupSection has one-to-many mapping with GroupNotes. I am trying to retrieve notes based upon GroupCanvas's primary key, but I am getting a Hibernate Lazy Initialization Exception. I tried out the recommendations on net, mostly SO, but none of them seem to help. Here is code.

DAO Method throwing error :

 @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 model :

@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 model 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;
    }
}

GroupNotes :

@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;}
}

Error log :

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)

What am I doing wrong, kindly let me know. If there is any more information required, kindly put a comment.

1

There are 1 answers

5
codedabbler On BEST ANSWER

Your JSON converter is executed after the Hibernate session is completed. The JSON converter is blindly accessing all the getters and setters, even the lazy ones. So when Hibernate tries to initialize GroupCanvas#ownedSection, there is no session available and hence this exception is thrown.

Possible solutions:

  1. Do not directly execute JSON converter on the Hibernate managed objects. Create DTO objects to do this job. DTO objects have no logic and are pure java beans and fit this role well. But the drawback is you have to maintain another class hierarchy. The benefits do outweigh the drawbacks. The following post can help with this approach:

    DTO pattern : Best way to copy properties between two Objects

  2. Use annotations to mark certain fields as not serializable. For example, JsonIgnore. The drawback with this is that if this field is ever needed in a different API, then you cannot use this.

  3. If one of the back-ref can be eliminated from your model (notes->section/section->canvas), then that makes it "friendlier" to serialization. In other works, JSON does not work well with cyclic references, so the lesser the amount of bi-directional/loop constructs the better it is. If it were not for the possibility of a cyclic reference, then you could initialize all the data necessary for serialization including GroupCanvas.