Fluent Entities With Hibernate and JPA
Fluent Entities With Hibernate and JPA
Fluent interfaces are a popular API design pattern in the Java world.
The goal of the pattern is to create APIs that are very easy to read,
and that define something similar to a domain-specific language. To
achieve that, the API heavily relies on method chaining so that the
code that uses the API flows and almost reads like prose.
A basic example of a fluent API might look like this:
APIs like these are often used for value classes and configuration
data. So, it’s no surprise that a lot of teams would like to use them for
entities.
Unfortunately, that’s not as easy as you might expect.
“In this case, for every persistent property property of type T of the
entity, there is a getter method, getProperty, and setter method
setProperty. For boolean properties, isProperty may be used as an
alternative name for the getter method.[2]
For single-valued persistent properties, these method signatures are:
• T getProperty()
• void setProperty(T t)”
JSR 338: JavaTM Persistence API, Version 2.2 – Section 2.2 (p. 24)
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
So, if you want to create JPA-compliant entities, you need to
implement public getter and setter methods for all entity attributes.
If you also wish to provide a fluent interface API, you can only add
these methods so that both APIs coexist on the same class.
The interesting part in this quote is “Hibernate does not require it”.
So, if you decide to ignore the JPA specification and potential
portability issues, Hibernate allows you to implement a clean, fluent
interface API for your entities.
JPA-Compliant Implementation
If you want to implement your entities in a JPA-compliant way, you
will not be able to design a nice and clean fluent interface API. You
can only add the fluent methods to your entity class, and keep the
getter and setter methods.
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
@Version
private int version;
@ManyToMany
private Set<Author> authors = new HashSet<Author>();
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
When you use the Book entity in your business code, you can decide
which API you want to use.
You can call the getter and setter methods.
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
Hibernate-Compliant Implementation
As described earlier, Hibernate recommends providing getter and
setter methods that follow the JavaBeans convention. But it doesn’t
require these methods.
That gives you higher flexibility when designing the API of your
entities. You can decide if you provide a fluent API by changing the
setter methods so that they return the entity object or if you provide
different methods instead.
Let’s take a look at both options.
@Id
@GeneratedValue
private Long id;
@Version
private int version;
@ManyToMany
private Set<Author> authors = new HashSet<Author>();
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
And you can also chain the calls of your setter methods to use them
fluently.
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
But this code snippet also shows the downside of this approach. Even
though you chain the method calls, the code still reads like you’re
calling multiple setter methods. It doesn’t flow.
You then don’t need to provide any setter or getter methods for your
entity attributes. That enables you to rename the setter methods to
create a real fluent interface.
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
@Version
private int version;
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
@ManyToMany
private Set<Author> authors = new HashSet<Author>();
As you can see in the following code snippet, the new method names
drastically improve the readability of the business code. It now
almost reads like you’re describing the Book and it’s no longer a list of
technical calls of setter methods.
www.thoughts-on-java.org
Fluent Entities with Hibernate and JPA
One thing you need to be aware of when you implement your API like
this is that the fluent interface API doesn’t affect your JPQL queries.
You still use the attribute names in your query. That might get a little
bit confusing because they no longer match the method names that
you use in your business code.
www.thoughts-on-java.org