Getting started with Blaze-Persistence Entity Views

Quick introduction to Blaze-Persistence Entity Views and usage examples

By Christian Beikov on 24 November 2016

Complex data models as can be encountered in nearly every bigger project will at some point require some kind of DTOs to avoid loading the full state of an entity. Depending on how good a mapping of an entity model to DTO model is implemented, the performance and maintainability will vary.

Normally, when implementing a DTO approach you do the following steps

  1. Define the structure of what you want to consume e.g. the DTO definition

  2. Think of a base query as source for the DTO instances

  3. Transform the base query so that it produces the projections needed for the DTOs

  4. Do the mapping from the ResultSet-like structure to the DTO

Now you might skip 3. and instead of thinking of a base query like you should in 2. you copy-paste some existing query, adapting only minor things, or write the query especially for that case.
You end up with a lot of boilerplate code and will likely live through some of these episodes

You try to use the entity model everywhere and try to avoid DTOs because they are a pain

This might work at first but it will break with infamous `LazyInitializationException`s sooner or later because you forget to fetch relations for some use cases.

You try to be smart and use an Object Mapper library like e.g. Dozer

That will help you with the boilerplate code for mapping data, but you still have to take care of the fetches otherwise you might run into the N + 1 queries problem.

Make the EntityManager available for the whole HTTP request to avoid `LazyInitializationException`s a.k.a Open Session in View antipattern

This will sooner or later lead to the infamous N + 1 queries problem because you lazy load collections somewhere.

Blaze-Persistence Entity Views will handle all of these issues thus increasing performance and maintainability.

Setup

Before you can start, you have to prepare your application. The setup section will guide you through the necessary steps depending on the environment in which you want to use Entity Views.

In order to get started, you need dependencies on the Core module, the Entity View module, the jpa provider integration and optionally the DI framework integration.

Here are the Maven dependencies for Hibernate and CDI.

<!-- Core module -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-core-api</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-core-impl</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>runtime</scope>
</dependency>

<!-- Entity View module -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-api</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-impl</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>runtime</scope>
</dependency>

<!-- Entity View CDI integration -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-integration-entity-view-cdi</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>runtime</scope>
</dependency>

<!-- Hibernate 5.2 integration -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-integration-hibernate-5.2</artifactId>
    <version>${blaze-persistence.version}</version>
    <scope>runtime</scope>
</dependency>

Entities

The entity model that we are going to test with is intentionally simple. A cat with a name, mother and father.

@Entity
public class Cat {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    @ManyToOne(optional = true)
    private Cat father;
    @ManyToOne(optional = true)
    private Cat mother;

    // getters and setter omitted
}

Entity Views

Now comes the interesting part, the Entity View for the entity.

@EntityView(Cat.class)
public interface CatView {
    @IdMapping("id")
    Integer getId();

    String getName();
}

The EntityView annotation declares on which entity type this Entity View is based on e.g. for which it provides a projection for. It must have an IdMapping containing the path expression to the attribute that should be used as identifier for this Entity View. Apart from that, you can declare any properties that you would like your Entity View to contain. It’s a DTO after all, so think about the target representation.

The name attribute declared via getName() could also use a more complex expression by declaring the expression in @Mapping, but by default, it uses just the attribute name as expression, so in this case name.

Entity View Usage

Using the Entity View is probably one of the nicest parts because it can be applied on any base query, as long as you can find an Entity View root for which the projection can de done.

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

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

The base query in JPQL as defined through cb would look like the following

SELECT
    theCat
FROM
    Cat theCat
WHERE theCat.father IS NOT NULL
  AND theCat.mother IS NOT NULL

Applying the Entity View on the CriteriaBuilder will result in an optimized query to fulfill the projection.

SELECT
    theCat.id,
    theCat.name
FROM
    Cat theCat
WHERE theCat.father IS NOT NULL
  AND theCat.mother IS NOT NULL

Although the example is very simple, you can already see that only the relevant attributes are fetched from the database. The benefits are

  • Better performance - since less content has to be fetched and transferred

  • Good reuse - as the projection is transparently applied on an existing base query

  • Less boilerplate - because you don’t have to write the data plumbing yourself

Behind the scenes, the Entity View module generates a simple POJO with final fields that are initialized via a generated constructor.

Conclusion

Although this article only touched the very basics of Entity Views, the benefits already reveal themselves. Mappings in Entity Views are done on the JPQL level which is a big benefit regarding cross RDBMS compatibility.

In an upcoming article I will show you how relations can be mapped as subviews and how that will help you with LazyInitializationException and N + 1 select problems.

Stay tuned!

blaze-persistence jpa entity-view java-ee spring cdi
comments powered by Disqus