|

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.

AnnotationJavaDoc
AnyMetaDefUsed to provide metadata about an Any or ManyToAny mapping.
ColumnTransformerCustom 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(?)”
FetchProfileDefine the fetching strategy profile.
FetchProfileDefine the fetching strategy profile.
FilterAdd filters to an entity or a target entity of a collection.
FilterDefFilter definition. Defines a name, default condition and parameter types (if any).
FilterJoinTableAdd filters to a join table collection.
GenericGeneratorGenerator annotation describing any kind of Hibernate generator in a generic (de-typed) manner.
JoinColumnOrFormulaAllows joins based on column or a formula. One of formula() or column() should be specified, but not both.
NamedNativeQueryExtends NamedNativeQuery with Hibernate features.
NamedQueryExtends NamedQuery with Hibernate features.
TableComplementary information to a table either primary or secondary.
TuplizerDefine a tuplizer for an entity or a component.
TypeDefA 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.