Entity View subview as rescue for LazyInitializationException

Solutions for handling LazyInitializationException problems with Blaze-Persistence Entity Views subviews

By Christian Beikov on 17 February 2017

In the last post I have introduced the basics of Entity Views. This time I will show how subviews can be used to overcome the infamous LazyInitializationException and N + 1 queries problems.

Nearly everyone who used JPA in his career already had the pleasure to experience problems associated with lazy initialization. Depending on the persistence provider (Hibernate, EclipseLink, etc.), the outcome of a lazy initialization might differ due to different default behaviors. Hibernate, for example, chooses to perform lazy initialization only on managed entity instances by default and throws a LazyInitializationException when it attempts to initialize an unmanaged entity. On the other hand, EclipseLink’s default behavior is to allow lazy initialization of unmanaged entities as well by opening a transient connection for the loading.

The problem with lazy initialization is that queries might be issued or exceptions thrown at a later stage when data is accessed which might be unexpected. There certainly are use cases where lazy initialization is a good fit, but providing data to e.g. a view layer is not.

Entity View model

The entity model is the same as in the last blog post.

@EntityView(Cat.class)
public interface SimpleCatView {

    @IdMapping("id")
    Integer getId();

    String getName();
}

@EntityView(Cat.class)
public interface CatView extends SimpleCatView {

    SimpleCatView getFather();

    SimpleCatView getMother();
}

The SimpleCatView just contains the name and the id. The CatView extends that by also mapping the father and mother relations as subview attributes. The relations mapped to the subview SimpleCatView thus only the name and the id of the relation are selected. The subview attribute will be null if the relation is also null.

Entity Views are declarative and allow for generating efficient JPQL queries. Let’s compare the entity view approach with a naive DTO implementation.

public class SimpleCatDTO {

    protected final Cat entity;

    public SimpleCatDTO(Cat entity) { this.entity = entity; }

    public Integer getId() { return entity.getId(); }
    public String getName() { return entity.getName(); }
}

public class CatDTO extends SimpleCatDTO {

    public CatDTO(Cat entity) { super(entity); }

    public SimpleCatDTO getFather() { return entity.getFather() == null ? null : new SimpleCatDTO(entity.getFather()); }
    public SimpleCatDTO getMother() { return entity.getMother() == null ? null : new SimpleCatDTO(entity.getMother()); }
}

When accessing e.g. the name of a father instance that has been detached, one might get a LazyInitializationException if the father is not initialized i.e. a proxy. This might not immediately be apparent because the father and mother instances might have been in an initialized state in the persistence context during querying. Fortunately, this can’t happen when using Entity Views because they rely completely on the scalar results of a query that perfectly fits the expected projection.

Using such an Entity View is the same as using any other.

CriteriaBuilder<Cat> cb = criteriaBuilderFactory.create(entityManager, Cat.class);
cb.from(Cat.class, "theCat");

EntityViewSetting<CatView, CriteriaBuilder<CatView>> setting = EntityViewSetting.create(CatView.class);
List<CatView> list = entityViewManager
                        .applySetting(setting, cb)
                        .getResultList();

Behind the scenes

The JPQL query generated for the shown code looks approximately like this.

SELECT
    theCat.id,
    theCat.name,
    father_1.id,
    father_1.name,
    mother_1.id,
    mother_1.name
FROM
    Cat theCat
LEFT JOIN
    theCat.father father_1
LEFT JOIN
    theCat.mother mother_1

As you can see, the query is quite efficient and on top of that you can stop worrying about object creation plumbing when implementing the DTO approach! Currently, every relation and attribute that is used within an Entity View is fetched with the fetch strategy JOIN. This is going to be configurable in a future version. Since no entities are used (i.e. only plain scalar values are used) there is no lazy loading involved and you don’t have to worry about LazyInitializationException problems. Subviews obviously can contain other subviews as well, you just aren’t allowed to introduce cycles.

When building the EntityViewManager the Entity Views are validated against the entity model, so using Entity Views is similar to using named queries from a safety perspective.

You can still use entity types in an Entity View, but we generally discourage such mappings because a subview is more appropriate most of the time. After all, using entity types reintroduces LazyInitializationException or N + 1 queries problems.

Conclusion

With subviews in your toolkit you can handle a lot of use cases very elegantly. Right now, subviews have only been used for mapping *ToOne relations, but in an upcoming article I am going to show how you can map collections (i.e. *ToMany relations) of your entity model in your entity views.

Stay tuned!

blaze-persistence jpa entity-view subview lazy-initialization-exception
comments powered by Disqus