Follow @vlad_mihalcea Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

In this article, we are going to see how we can build an entity in a fluent style API fashion when using JPA and Hibernate.

The JHipster development team wants to expose a Fluent Interface entity building methods for their JPA entities, so they asked me if this is going to work with JPA and Hibernate. While JPA is rather strict about entity getters and setter, Hibernate is more lenient in this regard.

I would be very happy to have your insight @vlad_mihalcea on https://t.co/2c9tylORh2 — JHipster (@java_hipster) August 3, 2016

JPA specification

The JPA 2.1 specification makes the following remark in regard to entity properties:

It is required that the entity class follow the method signature conventions for JavaBeans read/write

properties (as defined by the JavaBeans Introspector class) for persistent properties when property

access is used. 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)

The reason why we get such a requirement is because the JPA specification makes no assumption regarding how entities are going to be used. By adhering to the Java Bean specifications, entities can be introspected using Java Reflection by IDE tools or other frameworks that might expect this standard getter and setter signature.

Hibernate specification

For interoperability, Hibernate suggests using the Java Bean specification as much as possible. However, Hibernate is less strict about Java Bean method signatures, so we can design our setters so that they follow the Fluent Interface method signature.

Domain Model

Our Domain Model is going to use two entities: a parent (e.g. Post ) and a child (e.g. PostComment ), both using Fluent Interface setter-style methods.

The Post entity looks like this:

@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; public Post() {} public Post(String title) { this.title = title; } @OneToMany( cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post" ) private List<PostComment> comments = new ArrayList<>(); public Long getId() { return id; } public Post setId(Long id) { this.id = id; return this; } public String getTitle() { return title; } public Post setTitle(String title) { this.title = title; return this; } public List<PostComment> getComments() { return comments; } public Post addComment(PostComment comment) { comment.setPost(this); comments.add(comment); return this; } }

And the PostComment entity looks as follows:

@Entity(name = "PostComment") @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; private String review; private Date createdOn; @ManyToOne private Post post; public Long getId() { return id; } public PostComment setId(Long id) { this.id = id; return this; } public String getReview() { return review; } public PostComment setReview(String review) { this.review = review; return this; } public Date getCreatedOn() { return createdOn; } public PostComment setCreatedOn(Date createdOn) { this.createdOn = createdOn; return this; } public Post getPost() { return post; } public PostComment setPost(Post post) { this.post = post; return this; } }

Testing Time

With the Fluent Interface API in place, we can create a Post entity and three PostComment(s) like this:

Post post = new Post() .setId(1L) .setTitle("High-Performance Java Persistence") .addComment( new PostComment() .setReview("Awesome book") .setCreatedOn(Timestamp.from( LocalDateTime.now().minusDays(1) .toInstant(ZoneOffset.UTC)) ) ) .addComment( new PostComment() .setReview("High-Performance Rocks!") .setCreatedOn(Timestamp.from( LocalDateTime.now().minusDays(2) .toInstant(ZoneOffset.UTC)) ) ) .addComment( new PostComment() .setReview("Database essentials to the rescue!") .setCreatedOn(Timestamp.from( LocalDateTime.now().minusDays(3) .toInstant(ZoneOffset.UTC)) ) ); entityManager.persist(post);

Fetching the Post and the PostComment entities works just fine:

Post post = entityManager.find(Post.class, 1L); assertEquals(3, post.getComments().size());

The generic JPA alternative

If you worry about JPA portability, you could simply add the Fluent Interface method along Java Bean setters:

@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; public Post() {} public Post(String title) { this.title = title; } @OneToMany( cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post" ) private List<PostComment> comments = new ArrayList<>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Post id(Long id) { this.id = id; return this; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Post title(String title) { this.title = title; return this; } public List<PostComment> getComments() { return comments; } public Post addComment(PostComment comment) { comments.add(comment.post(this)); return this; } } @Entity(name = "PostComment") @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; private String review; private Date createdOn; @ManyToOne private Post post; public Long getId() { return id; } public PostComment setId(Long id) { this.id = id; return this; } public String getReview() { return review; } public void setReview(String review) { this.review = review; } public PostComment review(String review) { this.review = review; return this; } public Date getCreatedOn() { return createdOn; } public void setCreatedOn(Date createdOn) { this.createdOn = createdOn; } public PostComment createdOn(Date createdOn) { this.createdOn = createdOn; return this; } public Post getPost() { return post; } public void setPost(Post post) { this.post = post; } public PostComment post(Post post) { this.post = post; return this; } }

To take advantage of the fluent-style API, we just need to use the new Fluent Interface methods while avoiding the Java Bean setters which might be used by some other third-party tools:

Post post = new Post() .id(1L) .title("High-Performance Java Persistence") .addComment(new PostComment() .review("Awesome book") .createdOn(Timestamp.from( LocalDateTime.now().minusDays(1) .toInstant(ZoneOffset.UTC)) ) ) .addComment(new PostComment() .review("High-Performance Rocks!") .createdOn(Timestamp.from( LocalDateTime.now().minusDays(2) .toInstant(ZoneOffset.UTC)) ) ) .addComment(new PostComment() .review("Database essentials to the rescue!") .createdOn(Timestamp.from( LocalDateTime.now().minusDays(3) .toInstant(ZoneOffset.UTC)) ) ); entityManager.persist(post);

In fact, this is exactly how the JHipster team has thought of adding Fluent Interface entities.

Although this generic alternative is better from a portability perspective, if your enterprise application does not rely on Java Bean compliant setters, you are better off changing the setter signature according to the Fluent Interface pattern requirements.

Conclusion

The Fluent Interface pattern works just fine with Hibernate, so one more reason to consider it the JPA provider of choice.

Code available on GitHub.

Insert details about how the information is going to be processed DOWNLOAD NOW