Damien
Damien

Reputation: 4121

Hibernate Lazy Loading makes criteria slow to run

I am experiencing a problem with hibernate and lazy loading of objects. basically I want to load an class which has an eagerly loaded field and not load the lazy fields of child classes

Take the following QuestionVO class

    @Entity
@Table(name = "question")
public class QuestionVO extends BaseDAOVO implements Serializable {

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column(name = "questionText", unique = false, nullable = false, length = 4000)
    @Size(min = 3, max = 4000)
    @Pattern(regexp = MobileAppsRegexConstants.GENERAL_ALLOWED_CHARCHTERS, message = "Question Text Not valid.")
    private String questionText;

    @ManyToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name = "MENU_STYLE_ID", nullable = true)
    private MenuStyleVO menuStyle;

    }

Take the following MenuStyleVO class

   @Entity
@Table(name = "menu_style")
public class MenuStyleVO extends BaseDAOVO implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 3697798179195096156L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column(name = "menuStyleName", unique = false, nullable = false, length = 200)
    private String menuStyleName;

    @Column(name = "menuTemplate", unique = false, nullable = false, length = 200)
    private String menuTemplate;

    @OneToOne(fetch = FetchType.LAZY, optional=false)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="logo_id")
    @JsonProperty("logo")
    private ApplicationImageVO logo;


    }

And this ApplicationImageVO class

@Entity
@Table(name = "application_image")
public class ApplicationImageVO extends BaseDAOVO implements Serializable {

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

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "image1242x2208")
    @Cascade({ CascadeType.ALL })
    @JsonIgnore
    private SubmissionLauncherImagesVO launcherImage1242x2208;  

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "image1536x2048")
    @Cascade({ CascadeType.ALL })
    @JsonIgnore
    private SubmissionLauncherImagesVO launcherImage1536x2048;  

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "image2048x1536")
    @Cascade({ CascadeType.ALL })
    @JsonIgnore
    private SubmissionLauncherImagesVO launcherImage2048x1536;  

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "logo")
    @Cascade({ CascadeType.ALL })
    @JsonIgnore
    private MenuStyleVO menuStyleLogo; 

    }

If L load the QuestionVO class from the database using the following hibernate criteria code - all the lazy fields of MenuStyleVO and ApplicationImageVO are also loaded.

On complicated use cases, this results in this query getting very slow

public QuestionVO findMasterAppQuestionById(int id) {

    Criteria criteria = currentSession().createCriteria(QuestionVO.class);
    criteria.add(Restrictions.eq("id", id));

    QuestionVO questionVO  = (QuestionVO) criteria.uniqueResult();

    return questionVO;
}

What I am wondering is - would it be possible to load the QuestionVO class and its eager fields and tell hibernate to ignore lazy fields from the other classes bar those that are needed?

Cheers Damien

Upvotes: 1

Views: 1442

Answers (1)

Bonifacio
Bonifacio

Reputation: 1502

Last time we faced an issue like this we used a constructor on parent class, which use only the desired fields of determined query.

I can't remember in fully how constructor inside a jpql query works, but it must be something like this:

select new com.package.class(c.field1, c.field2) from com.package.class c

Remember, a constructor with same arguments must be present on the desired entity.

Pros: - Better query perfomance; - Can be replicated with other arguments;

Cons: - Pretty limited, you can only use this hack on the main entity you are querying; - Includes a constructor only for determined query, poor design;

Also, you should take a look on EnttyGraphs of JPA. Seems quite promising, but didn't work as desired in our project.

Btw, Hibernate has put us many times on performance issues, hope this hack help you, good luck!

Edit:

Why this pattern would help in performance issues? Basically, with the example i've showed before, you are not loading everything via Hibernate, only the two fields (field1 and field2) of the main entity. Without using a constructor you shoudn't be able to do that, because your query would not result in a collection of the desired entity, but in a collection of two objects each iteration (Object[]). Using the constructor pattern you are creating instances of the desired entity, but only selecting a few fields from database, and that's why this pattern can help you, you are returning a collection of the desired entity with only a few fields.

Upvotes: 1

Related Questions