Benefits of @Repeatable annotations in Hibernate 5.2
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
Hibernate 5.2 introduced several changes based on Java 8 features. In previous posts, I showed you how to get query results as a Stream and the support of DateTime API classes and Optional. In this post, I will have a look at a small change to some Hibernate annotations. Some of them are now @Repeatable which makes them much more comfortable to use.
What is @Repeatable and why should you like it?
Before I get to the Hibernate annotations, let’s have a quick look at @Repeatable. The idea of repeatable annotations is very simple, and I’ve no idea why we had to wait so long for it.
Sometimes, you want to apply the same annotation multiple times. Before Java 8, the only way to do that was to use an additional annotation and provide an array of the annotations you want to apply as a value. The most common example with JPA and Hibernate are the @NamedQueries and @NamedQuery annotation.
@Entity @NamedQueries({ @NamedQuery(name = “Book.findByTitle”, query = “SELECT b FROM Book b WHERE b.title = :title”), @NamedQuery(name = “Book.findByPublishingDate”, query = “SELECT b FROM Book b WHERE b.publishingDate = :publishingDate”) }) public class Book implements Serializable { … }
I never liked this approach. The @NamedQueries annotation doesn’t provide any benefit. The only reason it exists and why I have to add it to my entity is that I want to define multiple @NamedQuery. Java 8 finally provided a better solution. You can now define an annotation as repeatable and apply it multiple times without any wrapper annotations.
Which Hibernate annotations are repeatable?
It’s pretty easy to find all Hibernate annotations that should be repeatable. You just need to have a look at the org.hibernate.annotations package and find all annotations that wrap an array of other annotations as their value. The wrapped annotations are obviously the ones that should be repeatable.
I had a look at that package, and it seems like all are now repeatable. You can find a list of all of them and their JavaDoc description below.
And don’t be surprised about some annotation names. Hibernate provides its own version of a lot of JPA annotations, like the @NamedQuery annotation, to extend them with Hibernate-specific features.
Annotation | JavaDoc |
---|---|
AnyMetaDef | Used to provide metadata about an Any or ManyToAny mapping. |
ColumnTransformer | Custom SQL expression used to read the value from and write a value to a column. Use for direct object loading/saving as well as queries. The write expression must contain exactly one ‘?’ placeholder for the value. For example: read=”decrypt(credit_card_num)” write=”encrypt(?)” |
FetchProfile | Define the fetching strategy profile. |
FetchProfile | Define the fetching strategy profile. |
Filter | Add filters to an entity or a target entity of a collection. |
FilterDef | Filter definition. Defines a name, default condition and parameter types (if any). |
FilterJoinTable | Add filters to a join table collection. |
GenericGenerator | Generator annotation describing any kind of Hibernate generator in a generic (de-typed) manner. |
JoinColumnOrFormula | Allows joins based on column or a formula. One of formula() or column() should be specified, but not both. |
NamedNativeQuery | Extends NamedNativeQuery with Hibernate features. |
NamedQuery | Extends NamedQuery with Hibernate features. |
Table | Complementary information to a table either primary or secondary. |
Tuplizer | Define a tuplizer for an entity or a component. |
TypeDef | A type definition. Much like Type, but here we can centralize the definition under a name and refer to that name elsewhere. The plural form is TypeDefs. |
How to use @Repeatable annotations
With JPA and Hibernate versions before 5.2, you were not able to annotate an entity with multiple of the same annotations. If there was the need to do that, e.g. when you wanted to define multiple @NamedQuery for an entity, you had to provide them as the value to a @NamedQueries annotation.
@Entity @NamedQueries({ @NamedQuery(name = “Book.findByTitle”, query = “SELECT b FROM Book b WHERE b.title = :title”), @NamedQuery(name = “Book.findByPublishingDate”, query = “SELECT b FROM Book b WHERE b.publishingDate = :publishingDate”) }) public class Book implements Serializable { … }
That’s no longer required, if you use Hibernate’s version of the @NamedQuery annotation or of any other annotation listed in the previous section. As you can see below, you can now add multiple org.hibernate.annotations.NamedQuery annotations directly to the entity.
@Entity @NamedQuery(name = “Hibernate5Book.findByTitle”, query = “SELECT b FROM Hibernate5Book b WHERE b.title = :title”) @NamedQuery(name = “Hibernate5Book.findByPublishingDate”, query = “SELECT b FROM Hibernate5Book b WHERE b.publishingDate = :publishingDate”) public class Hibernate5Book implements Serializable { … }
Summary
Making all these annotations repeatable is just a small change in the Hibernate code and in the beginning, it might not look like a big deal. But as a regular Hibernate or JPA user, you know that you currently use wrapper annotations at almost all of your entities.
This has become obsolete with Hibernate 5.2 and I like that a lot. The wrapper annotations provide no additional value and reduce the readability of the code.