Nagilla Venkatesh
10 min readMay 31, 2018

--

Design Patterns for JPA and Hibernate

For the last several years, the Java Persistence API (JPA) specification (JSR 338) and its most popular implementation, the Hibernate object relational mapping (ORM) framework, have been widely used in Java software. They are used in Java EE, Jakarta EE, and Spring applications

to persist data in relational databases. So it’s no surprise that there are several well-established patterns and design principles you can follow to build efficient and maintainable persistence layers. In this article, I explain the reasons to use composition instead of inheritance, the repository and Data-Transfer Object (DTO) patterns, and the Open Session in View anti-pattern. These are probably the most commonly used patterns and should be known by all experienced developers. To follow along, you’ll need familiarity with the concepts and terminology of database access and JPA.

Let’s start with two structural patterns and principles that make your application easier to understand and maintain: the Composition over Inheritance pattern and the Repository pattern.

Composition over Inheritance Pattern

As an experienced Java developer, you are probably aware of all the discussions about composition and inheritance. Over the years, the Java world reached the consensus that, in general, you should prefer composition over inheritance for plain Java classes.

The consensus and all the arguments in favour of composition are also valid for JPA entity classes. And in addition to that, there is another important reason you should prefer composition when you model your entities, which I’ll explain in a moment.

Let’s start by noting that relational databases support only the concept of composition, but not the concept of inheritance. Composition models a “has a” association as foreign key associ- ations between one or more database tables. In your domain model, you can implement compo- sition with an annotation that defines the type of association, such as @ManyToMany, @ManyToOne, or @OneToMany and an attribute of the type List or Set or of the type of the associated entity. The following code snippet shows an example of a simple many-to-one association between an Employee and a Department entity:

@Entity
public class Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
...}

As you can see in this code, composition is not only well supported by relational databases but is also easy to map in your domain model. This makes it a great choice.

Inheritance, on the other hand, models an “is a” association. This kind of association can’t be modeled in a relational table model. That’s why the JPA specification defines a set of mapping strategies that enable you to map an inheritance hierarchy to one or more relational database tables. You can choose from the following:

  • A mapped superclass strategy, which maps all subclasses as entities to their own, independent database table without supporting polymorphic queries.
  • A table-per-class strategy, which models all classes, including the superclass, as entities and maps them to independent database tables. This strategy supports polymorphic queries.
  • A joined strategy, which maps the specific attributes of each entity to its own database table but does not include the attributes of the superclass. So, whenever you want to fetch one of the subentites, you need to join at least two tables: the one mapped by the superclass and one mapped by the subclass.
  • A single-table strategy, which maps all entities to the same database table. This table has columns for the attributes of all entities in the inheritance hierarchy.

ou probably know from your own experience that bridging such a huge conceptual gap isn’t an easy task and that it can’t be done perfectly.

Unfortunately, that’s also true for JPA’s inheritance mapping strategies. All four of them have their advantages and disadvantages, but none of them provides an ideal solution. The mapped superclass strategy doesn’t support polymorphic queries, which you would expect for a full-featured mapping of an inheritance hierarchy. The table-per-class strategy supports polymorphic
queries, but these are very inefficient and most often too slow to be used in complex applications. The joined strategy always requires at least one additional JOIN operation to retrieve subentities. The single-table strategy uses a simple and very efficient table model that doesn’t require any JOIN operations to retrieve an entity. But mapping all entities of the inheritance hierarchy to the same database table also has a disadvantage. Some of the columns are mapped by only one subclass and will be null for all records that are mapped to other classes of the inheritance hierarchy. This strategy, therefore, does not permit not-null constraints on columns that are not mapped by the superclass.

As you can see, composition does not introduce any additional mapping problems, but all inheritance mapping strategies have their trade-offs. So, when you model your next entity, be aware of these trade-offs and, if possible, avoid them by preferring composition over inheritance.

Repository Pattern

The Repository pattern is a well-established pattern in enterprise applications. The repository class contains all persistence-related code but no business logic. It provides methods to per- sist, update, and remove an entity or methods that instantiate and execute specific queries. The goal of this pattern is to separate your persistence-related code from your business code and to improve the reusability of your persistence-related code. It also makes your business code easier to read and write, because you can focus on solving business requirements instead of interacting with a database.

If you’re using Spring Data or Apache DeltaSpike, you’re probably already familiar with the Repository pattern. Both frameworks enable you to generate repositories easily for your entities. They can generate the most common create, read, update, and delete (CRUD) operations and custom queries based on interfaces and method signatures.

The following code snippet defines a repository by extending Spring Data’s CrudRepository interface and adds a method to load Employee entities with a given last name. Spring Data’s CrudRepository interface defines a set of methods for standard write operations, such as save, delete, and read operations. Spring Data generates a class that implements this interface. So, you don’t need to spend any additional effort to get the required functionality.

public interface EmployeeRepository
extends CrudRepository <Employee, Long> {
List<Employee> findByLastname(String lastname);
}

Apache’s DeltaSpike project provides you with similar functionality for Java EE and Jakarta EE applications.

In addition to the previous structural patterns, there are also several query patterns and anti- patterns that you should apply or avoid when you read your data from a relational database. I want to focus on the two most popular ones: the Open Session in View anti-pattern and the DTO pattern.

Open Session in View Antipattern

Opening the Hibernate Session in your view layer is an anti-pattern that has been around for years. Let’s start with a quick explanation. The general idea is simple: you open and close Hibernate’s Session in the view layer instead of in the business layer of your application. That enables you to trigger some business operations in your business layer and retrieve one or more entities that you use to render the result in your view layer. During this rendering step, you keep the Hibernate Session open so that Hibernate can load lazily initialized entity associations without throwing a LazyInitializationException.

Some of the main reasons for the popularity of this anti-pattern are that it’s very easy to use and it doesn’t cause any problems on small development systems or test systems. That benefit disappears when you deploy the application to production, where the initialization of these associations requires lots of additional queries. This problem is known as the n+1 select issue.

You can avoid these problems by using the DTO pattern, which I explain in the next section, or by controlling the Hibernate Session
inside your business layer.

The latter solution requires you to initialize all required associations inside
your business layer. That becomes necessary because the view layer can no longer access the Hibernate Session because it is already closed when the view layer starts the rendering operations. Because of this, each access to an uninitialized association throws a LazyInitializationException. The best way to avoid this exception is to initialize lazily fetched associations in your business layer. JPA and Hibernate offer several options for doing that. Let’s look at the two most popular ones:

  • The use of a JOIN FETCH clause in a JPQL query
  • The definition of a query-independent @NamedEntityGraph
    Initialize associations with a JOIN FETCH clause. A JOIN FETCH clause is the easiest option for initializing an association and is my recommendation for all use cases. You can use the JOIN

FETCH clause within a Java Persistence Query Language (JPQL) query or a CriteriaQuery. It tells your persistence provider to not only join the tables of the two associated entities within your query but also to initialize the association.

The following code snippet shows a simple JPQL query that fetches Department entities with their associated Employee entities:

TypedQuery<Department> q = em.createQuery(
"SELECT d FROM Department d LEFT JOIN FETCH d.employees", Department.class);

When you execute this query and activate Hibernate’s SQL query logging, the following SQL statement is written to your log file:

18:25:08,666 DEBUG [org.hibernate.SQL] -
select
department0_.id as id1_0_0_,
employees1_.id as id1_1_1_,
department0_.name as name2_0_0_,
department0_.version as version3_0_0_,
employees1_.department_id as departme5_1_1_,
employees1_.firstName as firstNam2_1_1_,
employees1_.lastName as lastName3_1_1_,
employees1_.version as version4_1_1_,
employees1_.department_id as departme5_1_0__,
employees1_.id as id1_1_0__
from
Department department0_
left outer join
Employee employees1_
on department0_.id=employees1_.department_id

As you can see, Hibernate not only selects the database columns mapped by the Department entity but also selects all columns mapped by the Employee entity within the same query. That’s a lot faster than executing an additional query to initialize the association for each selected Department entity.
Initialize associations with a NamedEntityGraph. If you load an entity via the find method
of your EntityManager or if you’re looking for a reusable way to define the fetching behavior, you can use a NamedEntityGraph. It was introduced in JPA 2.1 and provides an annotation-based approach to define a graph of entities that will be fetched from the database.

Here’s a simple example of a NamedEntityGraph that fetches all Employee entities associated with a Department entity:

@NamedEntityGraph (
name = "graph.DepartmentEmployee",
attributeNodes = @NamedAttributeNode("employees"))

After you have defined your NamedEntityGraph, you can use a query hint to tell your persistence

provider to use it as a fetchgraph with your query or your call of the EntityManager.find method.

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.fetchgraph",
em.getEntityGraph("graph.DepartmentEmployee"));
Department d = em.find(Department.class, 1L, hints);

The generated SQL statement is similar to the one generated for the previously explained JPQL query.

18:25:35,150 DEBUG [org.hibernate.SQL] -
select
department0_.id as id1_0_0_,department0_.name as name2_0_0_,
department0_.version as version3_0_0_,
employees1_.department_id as departme5_1_1_,
employees1_.id as id1_1_1_,
employees1_.id as id1_1_2_,
employees1_.department_id as departme5_1_2_,
employees1_.firstName as firstNam2_1_2_,
employees1_.lastName as lastName3_1_2_,
employees1_.version as version4_1_2_
from
Department department0_
left outer join
Employee employees1_
on department0_.id=employees1_.department_id
where
department0_.id=?

Data Transfer Object Pattern

DTO is another well-known and often used design pattern. It introduces one or more classes to model a data structure employed by a specific use case or by the API of your application. A DTO is a simple Java class that aims to transfer and provide access to its data in the most efficient way. The following code snippet shows an example of the EmployeeWithDepartment DTO, which stores the first name and last name of the employee and the name of the department:

public class EmployeeWithDepartment {

private String firstName;
private String lastName;
private String department;

public EmployeeWithDepartment(String firstName, String lastName,String department) {
this.firstName = firstName;
this.lastName = lastName;
this.department = department;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
} }

After you’ve defined your DTO, you can use it as a projection in your JPQL, Criteria, and native queries. JPA supports constructor expressions in JPQL and CriteriaQuery queries, and you can use @SqlResultSetMapping to map the result of your native queries for each retrieved record. In all cases, your persistence provider selects the specified database columns and calls the construc- tor referenced in the constructor expression for each record of the result set.

The following code snippet shows an example of a JPQL query using a constructor expres- sion. It consists of the keyword new followed by the fully referenced class name of the DTO and one or more entity attributes that define the parameters.

TypedQuery<EmployeeWithDepartment> q = em
.createQuery(
"SELECT new "
+ "org.thoughts.on.java.model.EmployeeWithDepartment("
+ " e.firstName, e.lastName, e.department.name) "
+ "FROM Employee e WHERE e.id = :id", EmployeeWithDepartment.class);
q.setParameter("id", 1L);
q.getSingleResult();

As you have seen, you can easily map each record of your query result to a DTO. But when should you use DTOs?

Reasons to use DTOs. If you implement and deploy your presentation and business layers inde- pendently of each other (for example, a Java microservice with a REST API and a JavaScript front end), you’ll want to create a stable API that doesn’t leak any internal information or design decisions. This enables you to adapt your business layer to new requirements or to improve the existing implementation without changing the API.

It also enables you to exclude some entity attributes from your API — for example, internal attributes that shouldn’t be visible to any user or huge lists of associated entities. Especially in REST APIs, it’s better to provide a link to another REST endpoint that provides you the requested resources instead of including them in the returned JSON document.

Use cases that require a few attributes of multiple, associated entities are also common rea- sons to use DTOs. You can, of course, load the entities with all their associations, but that is not as efficient as selecting only the attributes that you need for your use case.
Disadvantages of DTOs. The DTO design pattern also has a few disadvantages. DTOs introduce a lot of redundancy when they are identical to your entities. That creates additional effort when- ever you need to change or remove one of these attributes.

And as you probably know from your own experience, it’s difficult to decouple the API of CRUD use cases from the persistence layer. Even so, DTOs enable you to change your entities without changing your API. Real-world projects show that if you change your entities, you most often also need to change your DTOs. If you find yourself in a situation in which your DTO is identical to your entity and you’re always changing both of them, you should consider removing the DTO and using the entity instead.

Conclusion

Most Spring and Jakarta EE applications use JPA to implement their persistence layer. It’s no surprise that you can choose from several well-established design patterns that help you to implement a robust and efficient persistence layer.

In this article, I discussed why you should prefer composition over inheritance, looked at the repository and DTO patterns, and compared two options for initializing lazily fetched associations in the business layer to avoid the Open Session in View anti-pattern. These are just a few of the most commonly used patterns.

--

--