Saving Java enum in database as String

Saving enum values in a relational database is a bit more tricky than primitives, Strings, and regular immutable value wrappers. From this post, you’ll learn different methods for saving Java enums in a relational database using JPA. I’ll compare these methods and present the best application for them.

Advertisement

Sample enum to save

Since you found this article, you probably already have your own enum which you plan to store in a database. But for the demonstration purpose, we’re going to use a simple enum presented below.

enum Difficulty {
   LOW,
   NORMAL,
   HIGH
}

How JPA save enum by default

First of all, in order to save enum values in a relational database using JPA, you don’t have to do anything. By default, when an enum is a part of an entity, JPA maps its values into numbers using the ordinal() method.

What it means is that without customizations JPA stores enum value as numbers. These numbers are associated with the order in which you define values in the enum. In our sample enum, JPA will map LOW value to 0, NORMAL to 1, and HIGH to 2.

Enum as numbers – pros and cons

The main benefit of this approach is low use of memory. If you care about memory consumption, this might be your preferred approach.

However, there is one huge disadvantage in default JPA behavior when you plan to add more values to your enum.

You always must add new enum values at the end of the list. Otherwise, values which you stored in a database will be mapped to different values when you try reading them.

Imagine you added a new value called LOWEST as a first value in the Difficulty enum. From now, JPA will map all 0 values stored in a database to this new enum value even though you’ve never saved it. You can of course update all records in your database every time you do a change to the enum but it’s not very practical.

Additionally, readability of enum values directly in a database is poor. The reader needs to know the order of values in the enum to understand what is the meaning of individual numbers.

Summing up, because of potential bugs which default JPA enum mapping may cause, I recommend to avoid it. Fortunately, it’s very easy to override the default approach.

Saving enum as string with JPA

Computers prefer numbers for enumerations but humans understand the text better. Otherwise, we would use IP addresses instead of domain names, right?

JPA allows you to store enum values as text. You only need to inform JPA about your intention in an entity which holds the enum using annotation called @Enumerated. Let’s see an example:

@Entity
class Task {

   @Enumerated(EnumType.STRING)
   private Difficulty difficulty;
   //...

}

The annotation accepts EnumType as a parameter. This enum has two values: STRING and ORDINAL. As you probably suspect, STRING indicates that JPA should save enum values as text. Using ORDINAL acts as the default JPA behavior.

Pretty straightforward, isn’t it?

Enum as string – pros and cons

Because EnumType.STRING uses the name() method of the enum, changing the order of values in the enum doesn’t affect mappings for records already saved in a database. You also don’t have to add new values at the end of the list. What is more, records are also easier to read directly in a database.

The main drawback is higher memory consumption. If you think it’s a small cost for better fault tolerance, save enum as strings.

Best way to map enum – JPA attribute converts

If JPA attribute converters aren’t the best way to map Java enums, they’re certainly the most flexible.

Generally speaking, the @Enumerated annotation allows you to choose if JPA should map the values of an enum using its ordinal() or name() method. JPA attribute converters go one step further. Converters give you the option to implement your custom enum mapping strategy.

Actually, you can use attribute converters with any Java type, not only enums. Therefore, the knowledge you will get here has a wider application.

Converter needed to save enum in database?

Implementing JPA attribute converter

The attribute convert is defined as a Java interface. It has two generic type parameters which indicate input and output types for the converter. Implementing a converter requires writing two methods for mapping between these types in both directions.

An example is worth a thousand words.

Using @Enumerated(EnumType.STRING) isn’t null safe. Let’s create a custom convert between our sample enum and String so that we can remove enum values without causing an exception when fetching records already stored in a database but simply map them to null.

Here is how it looks:

@Converter
class PriorityJpaConverter implements AttributeConverter<Priority, String> {

   @Override
   public String convertToDatabaseColumn(Priority priority) {
       if (priority == null) {
           return null;
       }
       return priority.toString();
   }

   @Override
   public Priority convertToEntityAttribute(String string) {
       if (string == null) {
           return null;
       }
       try {
           return Priority.valueOf(string);
       } catch (IllegalArgumentException e) {
           return null;
       }
   }

}

The convertToDatabaseColumn() accept the enum as a parameter and calls its toString() method to convert the enum value to text. If the input is null, it isn’t converted. JPA uses this method when saving entities in a database.

The convertToEntityAttribute() method is used for read operations. It maps between the value stored in a database and the Java type in the corresponding entity attribute. The method map null inputs and non-existing enum values into nulls.

Using JPA attribute converter

Once you have your custom converter, you can use it in your entity with the enum attribute. All you have to do is to mark the attribute with the @Convert annotation which informs JPA which converter class should the framework use for mapping.

@Entity
class Task {

   @Convert(converter = PriorityJpaConverter.class)
   private Difficulty difficulty;
   //...

}

Attribute converter for enum – pros and cons

As I mentioned in the beginning, attribute converters are the most flexible solution for mapping enums. They give you whole control over the mapping process in both directions.

I presented one possible use of converters which is null-safe mapping. Since the mapping is completely custom, you can implement a strategy which exactly fits into your specific requirements.

Attribute converters are also easy to verify with unit tests. Since a converter is a simple Java class, you can test them in isolation without any JPA implementation.

What about the drawbacks?

I can think only about one disadvantage. You have to implement and test the converter. Yet, it’s a small price for the flexibility.

Conclusion

As you can see, a simple thing as saving Java enum in a database can be done in many different ways. At this point, you should know which saving enum strategy is the best for your requirement. If the @Enumerated annotation fits your needs, stick to it for its simplicity. Otherwise, pick the freedom and write a custom attribute converter.

I hope you found the article useful and worth spreading the word. If you don’t want to miss future articles, consider subscribing to my blog so I can inform you about the latest publications.

Facebooktwittergoogle_plusredditlinkedinmail
Advertisement

Leave a Reply