2

I am using JPA with hibernate in my spring boot application. Whenever I try to fetch the enities using jpa methods, its returning the entity plus all the association present inside it. I wanted to fetch the associated entities on demand(lazy loading), so I have provided fetch=FetchType.LAZY in my domain class. But still its returning all the entries.

Below is the code: Case.java

    @Entity
    @Table(name="smss_case")
    public class Case implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -2608745044895898119L;

    @Id
    @Column(name = "case_id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer caseId;

    @Column( name="case_title" )
    private String caseTitle;

    @JsonManagedReference
    @OneToMany(mappedBy="smmsCase", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
    private Set<Task> tasks;

    }

}

Task.java

@Entity
@Table(name="task_prop")
public class Task implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -483515808714392369L;

    @Id
    @Column(name = "task_id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer taskId;

    @Column(name="task_title")
    private String taskTitle;

    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn( name="case_id", nullable=false)
    private Case smmsCase;
// getters and setters
}

Service.java

public Case getCases(Integer id) {
        return dao.findById(1).get();
}

Dao.java

public interface ServiceDao extends JpaRepository<Case, Integer>{

}

{
"caseId":1, "caseTitle":"ergonomics", "tasks":[
{
"taskId":1, "taskTitle":"ca" }, {
"taskId":2, "taskTitle":"hazards" }, {
"taskId":3, "taskTitle":"remedy" } ] }

Any help will be highly appreciated!

Thanks!

Vishrant
  • 15,456
  • 11
  • 71
  • 120
grav
  • 21
  • 1
  • 5
  • What makes you think lazy loading doesn't work? My guess: you're serializing a Case to JSON, and you see its tasks in the JSON. Lazy loading doesn't mean "no loading". It means that when something (like the JSON serializer) calls a method of the set of tasks (to serialize them), then, and only then, JPA executes the query to load the tasks. Lazy loading is working fine. You just don"t understand what it means. Setting fetch=LAZY on a OneToMany is useless, BTW, since that's the default value. – JB Nizet Nov 25 '18 at 08:29
  • @ JB Nizet, Thanks for your response. I still have a confusion. No where in my service I am calling any methods of set of tasks. Still the getCases() method inside my Service is loading all the associated tasks. Please let me know whats going wrong here. Thanks! – grav Nov 25 '18 at 09:34
  • "when something (**like the JSON serializer**) calls a method of the set of tasks (**to serialize them**)". JSON serialization doesn't happen by magic. The JSON serializer goes through all the properties of your object to get their value and transform them to JSON. If they're a collection (like your set of tasks), it iterates through the collection, using their iterator() method. So that triggers the lazy loading. – JB Nizet Nov 25 '18 at 09:37
  • Can you share your controller code? Do you return your entity directly or some dto which represents it? It might be happened because of you return entity itself and the serializer calls related models while serialization. – Emre Savcı Nov 25 '18 at 09:43
  • @ JB Nizet, thank you so much!! You were right. Lazy loading is actually working but when I try to return the object from my API, it was calling the associated entities. @ Emre Savcı, Thanks for your response, I got it working. As mentioned, when the controller is returning the entity, object to json serialization is triggering the associations. To do this, I have configured MappingJackson2HttpMessageConverter. (reference: https://stackoverflow.com/questions/21708339/avoid-jackson-serialization-on-non-fetched-lazy-objects) – grav Nov 25 '18 at 12:54
  • @grav once you found a solution please consider adding it to the answer section, you can earn perks too with that ;) – Vishrant Jun 12 '19 at 16:19

1 Answers1

2

Was tricky to investigate but I had this issue as I was using mapstruct and it happens to do deep/ nested mapping and in the process, it calls getter of the lazy loaded property. The issue was resolved when I used mapstrct @BeforeMapping.

@Mapper
public interface HibernateAwareMapper {
    @BeforeMapping
    default <T> Set<T> fixLazyLoadingSet(Collection<?> c, @TargetType Class<?> targetType) {
        if (!Util.wasInitialized(c)) {
            return Collections.emptySet();
        }
        return null;
    }

    @BeforeMapping
    default <T> List<T> fixLazyLoadingList(Collection<?> c, @TargetType Class<?> targetType) {
        if (!Util.wasInitialized(c)) {
            return Collections.emptyList();
        }
        return null;
    }

   class Util {
       static boolean wasInitialized(Object c) {
           if (!(c instanceof PersistentCollection)) {
               return true;
           }

           PersistentCollection pc = (PersistentCollection) c;
           return pc.wasInitialized();
       }
   }
}

Ref. by kokorin

Vishrant
  • 15,456
  • 11
  • 71
  • 120