Implementing the builder pattern for your entities can massively improve the readability of your business code.

In contrast to the fluent interface pattern, there is nothing in the JPA specification or the Hibernate documentation that prevents you from implementing a builder for an entity class.

But implementing a good builder for your entities requires some additional thoughts. You need to do more than only provide builders that create a simple object. You need to cascade your builders to provide an API that makes it easy to build a graph of entities.

The Domain Model

Let’s create builders to create an Order with multiple OrderItems comfortably. Each OrderItem references a Product from the product catalog.

Class diagram of the domain model

Without a builder, the code to create an Order with 2 OrderItems looks like this.

Creating Builders for a Graph of Entities

The easiest approach to create a builder API for your entities would be to implement a builder for each entity class. But that API wouldn’t provide a lot of benefits compared to the constructor and setter methods that I used in the previous example.

A meaningful builder API needs to help you to create a graph of entity objects. For the example in this article, that means that you not only need to provide a builder for the Order and the OrderItem entity. You also need to support the creation of a Set of OrderItem objects for a given Order.

If you do that, you will be able to create a new Order with 2 OrderItems like this:

OK, let’s take a look at the code of the builder classes that I use in the code snippet.

The OrderBuilder

The Order entity is the root of the small graph of entities. When you create an Order object, you can ignore the primary key and the version attribute. These are generated attributes that you don’t need to provide when you instantiate a new object.

Due to that, you only need to provide a withOrderDate(LocalDate orderDate), a withItems(Set<OrderItem>items) and a buildOrder() method to be able to build an Order entity object.

Within these methods, you can perform additional validations, e.g., check that the orderDate isn’t in the past or that the Set of OrderItem isn’t empty.

They also enable you to hide technical details. I use that in the buildOrder method, to hide the double linking between the Order and OrderItem objects that’s required to manage the bidirectional one-to-many association.

Technically, we don’t need any additional methods. But as you can see in the code snippet, I also implement the withItems() method that returns an OrderItemListBuilder and doesn’t take any OrderItem entities as parameters.

The withItems() method and the OrderItemsBuilder class make the API much easier to use because you can use them to create new OrderItem objects and add them to the Order.

The OrderItemListBuilder

The OrderItemListBuider class bridges the gap between the Order and the OrderItemBuilder by managing the Set of OrderItems.

In contrast to the 2 other builders in this example, this class doesn’t implement any logic. It only provides the required glue code so that you can chain the method calls required to create multiple OrderItems and to add them to an Order.

There are 2 important things that I want to point out:

The reference to the OrderBuilder that gets provided as a constructor parameter.

The buildItemList() method that you need to call to get back to the OrderBuilder when you’re done adding OrderItems to the Set.

The OrderItemBuilder

The OrderItemBuilder implements the required methods to build an OrderItem.

In the first line, I instantiate a new OrderBuilder which I then use to provide the date of the order.

Then I want to add OrderItems to the Order. To do that, I first call the withItems() method. It returns an OrderItemListBuilder on which I call the addItem() method to get an OrderItemBuilder that’s linked to the OrderItemListBuilder. After I’ve set the reference to the Product entity and the quantity that the customer wants to order, I call the addToList() method. That method builds an OrderItem object with the provided information and adds it to the Set<OrderItem> managed by the OrderItemListBuilder. The method also returns the OrderItemListBuilder object. That allows me to either add another OrderItem to the Set or to call the buildItemList() to complete the creation of the Set.

In the final step, I call the buildOrder() method on the OrderBuilder to create the Order method. Within that method, a new Order object gets created, the Set of OrderItems gets filled, and each OrderItems gets associated with the Order entity.

After I’ve created the Order object, I provide it as a parameter to the persist method of the EntityManager. I’ve set the CascadeType of the orderItems association to PERSIST so that Hibernate automatically persists the associated OrderItem entities when I persist the Order entity.

Conclusion

You can easily apply the builder pattern to your entities. In contrast to the fluent interface pattern, you don’t need to work around any technical requirements defined by the JPA specification or the Hibernate documentation.

To implement a good builder API, you need to cascade your builders so that you can comfortably create one or more associated entities.