12. Mapping Java enum

This chapter is based on my two blog posts.

I adapted them for the book and our typical model of dogs and also refined them a bit – there is no need to read the original posts.

Before we embark on our journey we should mention that Java enums are not necessarily the best way to model various code lists and other enumerations. We will tackle this at the end of the chapter.

Typical way how an enumeration is stored in a DB is a numeric column mapped to enum. We all know that using EnumType.ORDINAL is one of those worse solutions as values returned by ordinal() get out of control quickly as we evolve the enum – and most of them do. EnumType.STRING is not loved by our DB admins (if you have them) – even though it’s easy to read for sure. Normalization suffers, enum constant renaming is a bit problem too. So we want to map it to numeric (for instance) but not ordinal value. In the entity class we can map it as a raw Integer or as an enum type – one way or the other we will need to convert back and forth.

We will map them as enum and demonstrate JPA 2.1 converters in the process – AttributeConverter<X,Y> to be precise.

Only the final version of the code as we get to it at the end of this chapter is included in the repository.

Naive approach

Let’s have our simple enum:

1 public enum Gender {
2   MALE,
3   FEMALE,
4   OTHER
5 }

And we have our entity that is mapping numeric column to our enum:

1 @Entity
2 public class Dog {
3   @Id private Integer id;
4 
5 //..
6   @Convert(converter = GenderConverter.class)
7   @Column(name = "gender")
8   private Gender gender;
9 }

This entity will be the same throughout all of our solutions, so we will not repeat it. Important line is the one with @Convert annotation that allows us to do conversion. All we have to do now is to implement the converter:

 1 import javax.persistence.AttributeConverter;
 2 import javax.persistence.Converter;
 3 
 4 /**
 5  * This is coupled too much to enum and you always have to change both
 6  * classes in tandem. That's a big STOP (and think) sign in any case.
 7  */
 8 @Converter
 9 public class GenderConverter implements AttributeConverter<Gender, Integer> {
10   @Override
11   public Integer convertToDatabaseColumn(Gender someEntityType) {
12     switch (someEntityType) {
13       case MALE:
14         return 0;
15       case FEMALE:
16         return 1;
17       default:
18         // do we need this?  it catches forgotten case when enum is modified
19         throw new IllegalArgumentException("Invalid value " + someEntityType);
20         // the value is valid, just this externalized switch sucks of course
21     }
22   }
23 
24   @Override
25   public Gender convertToEntityAttribute(Integer dbValue) {
26     switch (dbValue) {
27       case 0:
28         return Gender.MALE;
29       case 1:
30         return Gender.FEMALE;
31       case 2:
32         return Gender.OTHER;
33     }
34     // now what? probably exception would be better just to warn programmer
35     return null;
36   }
37 }

I revealed the problems in the comments. There may be just one reason to do it this way – when we need the enum independent from that dbValue mapping. This may be reasonable if the enum is used in many other contexts. But if storing it in the database is typical we should go for more cohesive solution. And in this case we will be just fine with encapsulation and put the stuff that changes on one place – into the enum.

Encapsulated conversion

We still need to implement AttributeConverter because it is kind of glue between JPA and our class. But there is no reason to leave the actual mapping in this infrastructure class. So let’s enhance the enum to keep the converter simple. Converter we want to see looks like this:

 1 @Converter
 2 public class GenderConverter implements AttributeConverter<Gender, Integer> {
 3   @Override
 4   public Integer convertToDatabaseColumn(Gender gender) {
 5     return gender.toDbValue();
 6   }
 7 
 8   @Override
 9   public Gender convertToEntityAttribute(Integer dbValue) {
10     // this can still return null unless it throws IllegalArgumentException
11     // which would be in line with enums static valueOf method
12     return Gender.fromDbValue(dbValue);
13   }
14 }

Much better, we don’t have to think about this class at all when we add values to enum. The complexity is now here, but it’s all tight in a single class:

 1 public enum Gender {
 2   MALE(0),
 3   FEMALE(1),
 4   OTHER(-1);
 5 
 6   private final Integer dbValue;
 7 
 8   Gender(Integer dbValue) {
 9     this.dbValue = dbValue;
10   }
11 
12   public Integer toDbValue() {
13     return dbValue;
14   }
15 
16   public static final Map<Integer, Gender> dbValues = new HashMap<>();
17 
18   static {
19     for (Gender value : values()) {
20       dbValues.put(value.dbValue, value);
21     }
22   }
23 
24   public static Gender fromDbValue(Integer dbValue) {
25     // this returns null for invalid value,
26     // check for null and throw exception if you need it
27     return dbValues.get(dbValue);
28   }
29 }

I saw also some half-solutions without the static reverse resolving, but I hope we all agree it goes into the enum. If it’s two value enum, you may start with switch in fromDbValue, but that’s just another thing to think about – and one static map will not kill anyone.

Now this works, so let’s imagine we need this for many enums. Can we find some common ground here? I think we can.

Conversion micro-framework

Let’s say we want to have order in these things so we will require the method named toDbValue. Our enums will implement interface ConvertedEnum:

 1 /**
 2  * Declares this enum as converted into database, column value of type Y.
 3  *
 4  * In addition to implementing {@link #toDbValue()} converted enum should also
 5  * provide static method for reverse conversion, for instance
 6  * {@code X fromDbValue(Y)}. This one should throw {@link
 7  * IllegalArgumentException} just as {@link Enum#valueOf(Class, String)} does.
 8  * Check {@link EnumAttributeConverter} for helper methods that can be used
 9  * during reverse conversion.
10  */
11 public interface ConvertedEnum<Y> {
12     Y toDbValue();
13 }

It’s parametrized, hence flexible. Javadoc says it all – we can’t enforce the static stuff, because that’s how Java works. While I suggest that reverse fromDbValue should throw IllegalArgumentException, I’ll leave it to return null for now – just know that I’m aware of this. I’d personally go strictly for exception but maybe you want to use this method elsewhere in the code and null works fine for you. We will enforce the exception in our converter instead.

What are the changes in the enum? Minimal really, just add implements ConvertedEnum<Integer> and you can add @Override for toDbValue method. Not worth the listing. Now to utilize all this we need a base class implementing AttributeConverter – here it goes:

 1 /**
 2  * Base implementation for converting enums stored in DB.
 3  * Enums must implement {@link ConvertedEnum}.
 4  */
 5 public abstract class EnumAttributeConverter<X extends ConvertedEnum<Y>, Y>
 6   implements AttributeConverter<X, Y>
 7 {
 8   @Override
 9   public final Y convertToDatabaseColumn(X enumValue) {
10     return enumValue != null ? enumValue.toDbValue() : null;
11   }
12 
13   @Override
14   public final X convertToEntityAttribute(Y dbValue) {
15     return dbValue != null ? notNull(fromDbValue(dbValue), dbValue) : null;
16   }
17 
18   protected abstract X fromDbValue(Y dbValue);
19 
20   private X notNull(X x, Y dbValue) {
21     if (x == null) {
22       throw new IllegalArgumentException("No enum constant" +
23         (dbValue != null ? (" for DB value " + dbValue) : ""));
24     }
25     return x;
26   }
27 }

With this our concrete converters only need to provide reverse conversion that requires static method call to fromDbValue:

1 @Converter
2 public class GenderConverter extends EnumAttributeConverter<Gender, Integer> {
3   @Override
4   protected Gender fromDbValue(Integer dbValue) {
5     return Gender.fromDbValue(dbValue);
6   }
7 }

What a beauty suddenly! Converter translates null to null which works perfectly with optional attributes (nullable columns). Null checking should be done on the database level with optional validation in an application as necessary. However, if invalid non-null value is provided we will throw aforementioned IllegalArgumentException.

Refinement?

And now the last push. What is repeating? And what we can do about it?

It would be cool to have just a single converter class – but this is impossible, because there is no way how to instruct the converter about its types. Especially method convertToEntityAttribute is immune to any approach because there is nothing during runtime that can tell you what the expected enum type would be. No reflection or anything helps here, so it seems.

So we have to have separate AttributeConverter classes, but can we pull convertToEntityAttribute into our EnumAttributeConverter? Not easily really, but we’ll try something.

How about the static resolving? Can we get rid of that static initialization block in our enum? It is static, so it seems difficult to abstract it away – but indeed, we can do something about it.

Let’s try to hack our converters first. We need to get the type information into the instance of the superclass. It can be protected field like this:

1 public abstract class EnumAttributeConverter<X extends ConvertedEnum<Y>, Y>
2   implements AttributeConverter<X, Y>
3 {
4   protected Class<X> enumClass;

And subclass would initialize it this way:

1 public class GenderConverter extends EnumAttributeConverter<Gender, Integer> {
2   {
3     enumClass = Gender.class;
4   }

But this is not enforced in any way! Rather use abstract method that must be implemented. In abstract converter:

1 protected abstract Class<X> enumClass();

And in concrete class:

1 public class GenderConverter extends EnumAttributeConverter<Gender, Integer> {
2   @Override
3   protected Class<Gender> enumClass() {
4     return Gender.class;
5   }
6 }

But we’re back to 3 lines of code (excluding @Override) and we didn’t get to ugly unified convertToEntityAttribute:

 1 @Override
 2 public X convertToEntityAttribute(Y dbValue) {
 3   try {
 4     Method method = enumClass().getMethod("fromDbValue", dbValue.getClass());
 5     return (X) method.invoke(null, dbValue);
 6   } catch (IllegalAccessException | InvocationTargetException |
 7     NoSuchMethodException e)
 8   {
 9     throw new IllegalArgumentException("...this really doesn't make sense", e);
10   }
11 }

Maybe I missed something on the path, but this doesn’t sound like good solution. It would be if it lead to unified converter class, but it did not. There may be one more problem with hunt for unified solution. While the concrete implementation contains methods that have concrete parameter and return types, unified abstract implementation don’t. They can use the right types during runtime, but the method wouldn’t tell you if you used reflection. Imagine JPA checking this. Right now I know that unified public final Y convertToDatabaseColumn(X x) {...} works with EclipseLink, but maybe we’re asking for problems. Let’s check it really:

1 // throws NoSuchMethodException
2 // Method method = converter.getClass().getMethod(
3 //   "convertToDatabaseColumn", Integer.class);
4 Method method = converter.getClass().getMethod(
5   "convertToDatabaseColumn", Object.class);
6 System.out.println("method = " + method);

This prints:

1 method = public java.lang.Object
2   EnumAttributeConverter.convertToDatabaseColumn(java.lang.Object)

If something strictly matches the method types with column/enum types, we may have a misunderstanding because our method claims it converts Object to Object. Sometimes too smart may be just that – way too smart.

Simplified static resolving

Anyway, let’s look into that enum’s static resolving. This is actually really useful. Without further ado, this is how enum part may look like:

1 // static resolving:
2 public static final ConvertedEnumResolver<Gender, Integer> resolver =
3   new ConvertedEnumResolver<>(Gender.class);
4 
5 public static Gender fromDbValue(Integer dbValue) {
6     return resolver.get(dbValue);
7 }

So we got rid of the static map and static initializer. But the code must be somewhere:

 1 package support;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 /**
 7  * Helps reverse resolving of {@link ConvertedEnum} from a DB value back to
 8  * enum instance. Enums that can be resolved this way must have unified
 9  * interface in order to obtain {@link ConvertedEnum#toDbValue()}.
10  *
11  * @param <T> type of an enum
12  * @param <Y> type of DB value
13  */
14 public class ConvertedEnumResolver<T extends ConvertedEnum<Y>, Y> {
15 
16   private final String classCanonicalName;
17   private final Map<Y, T> dbValues = new HashMap<>();
18 
19   public ConvertedEnumResolver(Class<T> enumClass) {
20     classCanonicalName = enumClass.getCanonicalName();
21     for (T t : enumClass.getEnumConstants()) {
22       dbValues.put(t.toDbValue(), t);
23     }
24   }
25 
26   public T get(Y dbValue) {
27     T enumValue = dbValues.get(dbValue);
28     if (enumValue == null) {
29       throw new IllegalArgumentException("No enum constant for dbValue " +
30         dbValue + " in " + classCanonicalName);
31     }
32     return enumValue;
33   }
34 }

And this I actually really like. Here I went for strict checking, throwing exception. Without it or without needing/wanting the type name it could be even shorter. But you write this one once and save in each enum.

So there are three players in this “framework”:

  • ConvertedEnum – interface for your enums that are converted in this way.
  • ConvertedEnumResolver – wraps the reverse mapping and saves you most of the static lines in each converted enum.
  • EnumAttributeConverter – implements both-way conversion in a unified way, we just need to implement its abstract fromDbValue method. Just be aware of potential problems if something introspect the method parameter and return types because these are “generified”.

Alternative conversions within entities

So far we were demonstrating this all in the context of JPA 2.1. I’d like to at least mention alternative solutions for older JPA versions.

If you use AccessType.FIELD then the fields must be strictly in a supported type (that is some raw type, e.g. Integer for enum), but accessors can do the conversion. If you use AccessType.PROPERTY than the fields can be in your desired type and you can have two sets of accessors – one for JPA and other working with the type you desire. You need to mark the latter as @Transient (or break get/set naming convention).

Long time ago we used this technique to back Dates by long fields (or Long if nullable). This way various tools (like Sonar) didn’t complain about silly mutable Date breaking the encapsulation when used directly in get/set methods, because it was not stored directly anymore. JPA used get/set for Date, and we had @Transient get/set for millis long available. We actually preferred comparing millis to before/after methods on Date but that’s another story. The same can be used for mapping enums – just have JPA compatible type mapped for JPA and @Transient get/set with enum type. Most of the stuff about enum encapsulation and static resolving still applies.

Java 8 and reverse enum resolution

I focused mostly on conversion to values and back in the context of JPA 2.1 converters. Now we’ll focus on the part that helped us with “reverse resolution”. We have the value – for instance an int, but not ordinal number, of course! – that represents particular enum instance in the database and you want that enum instance. Mapping between these values and enum instances is a bijection, of course.

Our solution so far works fine but there are two limitations:

  • ConvertedEnumResolver depends on the common interface ConvertedEnum our enums must implement hence it is implicitly tied to the conversion framework.
  • If we need another representation (mapping) to different set of values we have to develop new resolver class.

The first one may not be a big deal. The second one is a real thing though. I worked on a system where enum was represented in the DB as int and elsewhere as String – but we didn’t want to tie it to the Java enum constant strings. New resolver class was developed – but is another class really necessary?

Everything in the resolver class is the same – except for the instance method getting the value on the enum. We didn’t want to use any reflection, but we had recently switched to Java 8 and I’d heard it has these method references! If we can pass it to the resolver constructor… is it possible?

Those who know Java 8 know that the answer is indeed positive. Our new resolver will look like this (I renamed it since the last time):

 1 /**
 2  * Helps reverse resolving of enums from any value back to enum instance.
 3  * Resolver uses provided function that obtains value from enum instance.
 4  *
 5  * @param <T> type of an enum
 6  * @param <Y> type of a value
 7  */
 8 public final class ReverseEnumResolver<T extends Enum, Y> {
 9   private final String classCanonicalName;
10   private final Map<Y, T> valueMap = new HashMap<>();
11 
12   public ReverseEnumResolver(Class<T> enumClass,
13     Function<T, Y> toValueFunction)
14   {
15     classCanonicalName = enumClass.getCanonicalName();
16     for (T t : enumClass.getEnumConstants()) {
17       valueMap.put(toValueFunction.apply(t), t);
18     }
19   }
20 
21   public T get(Y value) {
22     T enumVal = valueMap.get(value);
23 
24     if (enumVal == null) {
25       throw new IllegalArgumentException("No enum constant for '" +
26         value + "' in " + classCanonicalName);
27     }
28     return enumVal;
29   }
30 }

There is no conversion mentioned anymore. Just reverse resolving. So, our new enum does not have to be “Converted” anymore – unless you still need this interface for JPA conversion (which you may). Let’s show complete listing of our Gender enum that this time can be converted in two sets of values. Notice those method references:

 1 public enum Gender implements ConvertedEnum<Integer> {
 2   MALE(0, "mal"),
 3   FEMALE(1, "fem"),
 4   OTHER(-1, "oth");
 5 
 6   private final Integer dbValue;
 7   private final String code;
 8 
 9   Gender(Integer dbValue, String code) {
10     this.dbValue = dbValue;
11     this.code = code;
12   }
13 
14   @Override
15   public Integer toDbValue() {
16     return dbValue;
17   }
18 
19   public String toCode() {
20     return code;
21   }
22 
23   // static resolving:
24   public static final ReverseEnumResolver<Gender, Integer> resolver =
25     new ReverseEnumResolver<>(Gender.class, Gender::toDbValue);
26 
27   public static Gender fromDbValue(Integer dbValue) {
28     return resolver.get(dbValue);
29   }
30 
31   // static resolving to string:
32   public static final ReverseEnumResolver<Gender, String> strResolver =
33     new ReverseEnumResolver<>(Gender.class, Gender::toCode);
34 
35   public static Gender fromCode(String code) {
36     return strResolver.get(code);
37   }
38 }

So this is how we can have two different values (dbValue and code) resolved with the same class utilizing Java 8 method references.

If we wanted to store dbValue into one DB table and code into another table we would need two different converters. At least one of them wouldn’t be able to utilize ConvertedEnum contract anymore. Being on Java 8 I’d again use method references in converter subclass to specify both conversion methods.

Let’s say our universal new AbstractAttributeConverter looks like this:

 1 /** Base implementation for converting values in DB, not only for enums. */
 2 public abstract class AbstractAttributeConverter<X, Y>
 3   implements AttributeConverter<X, Y>
 4 {
 5   private final Function<X, Y> toDbFunction;
 6   private final Function<Y, X> fromDbFunction;
 7 
 8   public AbstractAttributeConverter(
 9     Function<X, Y> toDbFunction, Function<Y, X> fromDbFunction)
10   {
11     this.toDbFunction = toDbFunction;
12     this.fromDbFunction = fromDbFunction;
13   }
14 
15   @Override
16   public final Y convertToDatabaseColumn(X enumValue) {
17     return enumValue != null ? toDbFunction.apply(enumValue) : null;
18   }
19 
20   @Override
21   public final X convertToEntityAttribute(Y dbValue) {
22     return dbValue != null ? fromDbFunction.apply(dbValue) : null;
23   }
24 }

Notice that I dropped previous notNull enforcing method because our reverse resolver does it already (it still allows null values). The converter for Gender enum would be:

1 @Converter
2 public class GenderConverter extends AbstractAttributeConverter<Gender, Integer> {
3   public GenderConverterJava8() {
4     super(Gender::toDbValue, Gender::fromDbValue);
5   }
6 }

The only thing the abstract superclass does now (after dropping notNull) is that ternary expression treating null values. So you may consider it overkill as of now. On the other hand it prepares everything in such a way that concrete converter is really declarative and as concise as possible (considering Java syntax). Also notice that we made ConvertedEnum obsolete.

In this last section we underlined how handy some Java 8 features can get. Method references allowed us to create more flexible reverse resolver which we can use for multiple mappings, it allowed us to declare concrete attribute converters in a super short manner where nothing is superfluous and we could get rid of contract interface for our enums.

Relational concerns

Mapping enumerated values from Java to DB is a more complex issue than just the conversion. The enumeration can be based on an existing dedicated table or it can exist exclusively in Java world. It may or may not change over time. All these factors play a role in how complicated things can get.

If DB is “just a store” and application is driving the design you may be happy with encoding enums into a DB column as discussed throughout this chapter. But if we want to query the data using meaningful values (like MALE instead of 0) or we want to get this meaningful description into the outputs of SQL queries we’re asking for complications.

We may use enum names directly which is supported by JPA directly using EnumType.STRING but this has a couple of disadvantages:

  • It takes more space than necessary.
  • In case we decide to rename the constant we have to synchronize it with the code deployment. Alternatively we can use converter to support both values in DB for some time – this may be necessary when we’re not able to rename the data quickly, e.g. there’s too much of it.

Actually, if we mention renaming in front of our DB admins they will want to put enum values into another table as it makes renaming a non-issue (at least from DB perspective). We use FK instead of the names. We can also use a single lookup table for all enum types or even more sophisticated meta-data facility. In every case it comes with a cost though and it virtually always violates DRY principle (don’t repeat yourself).

Often we are extending a legacy application where the DB schema is a given and we have to deal with its design. For some code list tables we may mirror the entries in an enum because it does not change that much (e.g. gender). This will save us a lot of troubles in the Java code, giving us all the power of enums. Other lists are not good candidates (e.g. countries or currencies) as they tend to change occasionally and are also much longer. These should be mapped normally as entities.

In this chapter we focused only on mapping enums to arbitrary values and back, but before mapping something that looks like an enumeration into the Java code always think twice whether Java enum is the best way. Especially if that enumeration has its own dedicated table.