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
Reference: | JPA 2.1 Type Converter – The better way to persist enums from our JCG partner Thorben Janssen at the Some thoughts on Java (EE) blog. |
See also: http://www.nurkiewicz.com/2013/06/mapping-enums-done-right-with-convert.html