JPA 2.1 Type Converter - The better way to persist enums

Persisting enums with JPA 2.0 is possible, but there is no nice way to do it. Using the @Enumerated annotation, you can use EnumType.ORDINAL or EnumType.STRING to map the enum value to its database representation. But both options have some drawbacks, that we will discuss in the first part of this article. In the second part, I will show you to avoid these drawbacks by using a JPA 2.1 Type Converter.


Persisting enums with JPA 2.0

EnumType.ORDINAL uses the return value of Enum.ordinal() to persist the enum. So the first value of the enum will be mapped to 0, the second to 1 and so on. While this looks compact and easy to use in the first place, it causes problems when changing the enum. Removing enum values or adding a new value somewhere in between will change the mapping of all following values, e.g.:
before:
Vehicle:
CAR     -> 0
TRAIN   -> 1
PLANE   -> 2
after:
Vehicle:
CAR     -> 0
BUS     -> 1
TRAIN   -> 2
PLANE   -> 3
Adding Bus at the second position would require a database update to fix the enum mapping.

EnumType.STRING looks like a better option. It uses the String representation of the enum to persist it in the database. So adding or removing values will not change the mapping. But this representation can be quite verbose and renaming an enum value will break the mapping.
before: 
Vehicle:
CAR     -> CAR
TRAIN   -> TRAIN
PLANE   -> PLANE
after:
Vehicle:
CAR     -> CAR
BUS     -> BUS
TRAIN   -> TRAIN
PLANE   -> PLANE
 

Using JPA 2.1 Type Converter

JPA 2.1 Type Converter provide a third and in my opinion the best option. A Type Converter allows us to implement methods to convert the value of an entity attribute to its database representation and back. I will not get into too much details on how to implement a Type Converter because I already did this in one of my former articles.
By implementing our own mapping, we can choose a compact database representation and make sure, that changing the enum in any way will not break the existing mapping. The following example shows how to implement a type converter for the Vehicle enum:

@Converter(autoApply = true)
public class VehicleConverter implements AttributeConverter<Vehicle, String> {

 @Override
 public String convertToDatabaseColumn(Vehicle vehicle) {
  switch (vehicle) {
  case BUS:
   return "B";
  case CAR:
   return "C";
  case PLANE:
   return "P";
  case TRAIN:
   return "T";
  default:
   throw new IllegalArgumentException("Unknown value: " + vehicle);
  }
 }

 @Override
 public Vehicle convertToEntityAttribute(String dbData) {
  switch (dbData) {
  case "B":
   return Vehicle.BUS;
  case "C":
   return Vehicle.CAR;
  case "P":
   return Vehicle.PLANE;
  case "T":
   return Vehicle.TRAIN;
  default:
   throw new IllegalArgumentException("Unknown value: " + dbData);
  }
 }

}

The VehicleConverter maps the enum value to a one character String. By declaring it with @Converter(autoApply = true), we tell the JPA provider to use this Type Mapper to map all Vehicle enums. So we do not need to specify the converter at each entity attribute of type Vehicle.

But there is one thing we need to take care of and if you have read my former article about JPA Type Converter you might have wondered already. Type Converter cannot be applied to attributes annotated with @Enumerated. So we have to make sure that there is no @Enumerated annotation at our entity attributes of type Vehicle.

Conclusion

We implemented a simple Type Converter that uses our own rules to convert the Vehicle enum to its database representation. So we can make sure that changing the values of the Vehicle enum will not break the existing/remaining mappings.
If you want to try it on your own, you can find the source code on github: https://github.com/somethoughtsonjava/JPA2.1-EnumConverter

What do you think about using JPA Type Converter to persist enums? Please leave me a comment!

If you enjoyed reading this article and like to learn more about other Java EE7 features, make sure to subscribe to my RSS feed or follow me on twitter or google+.

And if you like to read more about the new JPA 2.1 features, make sure to have a look at my other articles ...

4 comments:

  1. Maybe the best solution would be use a generic type converter in order to manage all enums. In your example for each enum we have to create a converter.

    ReplyDelete
    Replies
    1. Hi Gamal,

      thanks for your comment.
      I would love to create a generic type converter. But up to now, I have no idea how to implement the convertToEntityAttribute(String dbData) method in a generic way. The method has to create a specific enum and I have no idea how to get the required one. Any suggestions?

      Regards,
      Thorben

      PS: If you want to try something, you can find the source on github: https://github.com/somethoughtsonjava/JPA2.1-EnumConverter

      Delete
  2. Even nicer would be to add a visitor to the enum and to use it inside the converter. That way the compiler would notify you in case the enum has changed.

    ReplyDelete
    Replies
    1. Hi Frisian

      You are right, using a visitor would be even better. I will use that for my real applications. Thanks!

      Regards,
      Thorben

      Delete