I’m Thorben Janssen from thoughts-on-java.org. JPA and Hibernate provide 2 standard options
to map an Enum to a database column. You can either use its String representation or its
ordinal value. But what do you do, if you need to map values
from a legacy database that don’t match these standard mappings?
Or if you don’t want to accept the drawbacks of the standard mapping?
The String representation is verbose, and renaming an enum value requires you also to
update your database. The ordinal of an enum value is its position
in the enum declaration. This value changes and requires you to update your database when
you remove an existing value or don’t add new values to the end of the enum declaration. In all these situations, you need to define
a custom mapping. But no worries, that’s pretty easy since JPA 2.1 introduced AttributeConverter. Let’s talk about AttributeConverters in
general before we dive into the details of the custom enum mapping.
AttributeConverter were introduced in JPA 2.1. They allow you to implement methods to
convert between the database and the Java representation of an attribute type.
You can use it to convert all basic attributes defined by an entity class, a mapped superclass,
or an embeddable class. The only exceptions are id attributes, version attributes, relationship
attributes and attributes annotated with Temporal or Enumerated. The implementation of an AttributeConverter
is pretty simple. You just need to annotate the class with @Converter and implement the
AttributeConverter interface with its 2 methods convertToDatabaseColumn and convertToEntityAttribute.
You can see that here on the slide. X is the entity attribute type you want to persist
and Y the type you want to store in the database. Enough theory. Let’s jump into the IDE and
implement such an AttributeConverter. Here you can see the AuthorStatus enum which
I want to persist in the database. I use it in the Author enum to indicate if an Author
published a book with a publishing house, or as a self-published book or if she’s
still writing the book and hasn’t published it yet.
You can see here that I don’t annotate it with the @Enumerated annotation as you would
normally do. As I said earlier, you can’t use AttributeConverter with attributes that
are annotated with @Enumerated. And you don’t need that annotation because you provide your
own mapping of the enum values. I implement that mapping with this AttributeConverter.
It implements the AttributeConverter interface and converts AuthorStatus enums to Strings.
I also annotated it with @Converter and set the autoApply attribute to true. That tells
Hibernate to apply this converter to all entity attributes of type AuthorStatus.
Hibernate calls the convertToDatabaseColumn to map an AuthorStatus enum value to its String
representation. The implementation of this method is simple. I use a switch statement
to define the mapping of each enum value to a String representation and throw an IllegalArgumentException
if there is no specific mapping for an enum value. You could also store the String representation
as an enum attribute but I decided against it because I just need it for the database
mapping and don’t want to pollute the domain model with it. And this method writes a short
log message for each mapping. You obviously don’t need that in your application, but
it allows me to show the mapping in the log file. The convertToEntityAttribute method provides the inverse mapping and Hibernate calls it
when it retrieved a value from the database. This time, I use a switch statement to map
each String to its enum value. OK, that’s all you need to do to implement
a custom enum mapping. As I said earlier, Hibernate applies the AttributeConverter automatically
because I set the autoApply attribute of the Converter annotation to true.
So, let’s persist and read an Author entity and see what happens. I do that in this test
case. I first create a new Author entity, set its
attributes and persist it in the database. Then I get a new EntityManager, start the
transaction and call the EntityManager.find method to read the Author entity from the
database. You can see the SQL INSERT statement here
and the log message written by the AttributeConverter in the following line. It converted the AuthorStatus
enum “PUBLISHED” to its database representation “P”.
And then I read the same entity from the database with this SQL SELECT statement. You can see
the log message of the AttributeConverter in the following line. It mapped the String
“P” to the AuthorStatus enum “PUBLISHED”. OK, that’s it for today.
If you want to learn more about Hibernate, you should join the free Thoughts on Java
Library. It gives you free access to a lot of member-only content like an ebook about the Java 8 support in Hibernate 5. I’ll add the link to
it to the video description below. And if you like today’s video, please give
it a thumbs up and subscribe below.