3 ways to serialize Java Enums

This article shows you best practice for serializing Java enums to database (via Hibernate, JPA) or file. It discusses three ways to serialize enums with code examples, the pros and cons of each way and also recommends the best way. You should know this if you are serializing an Object with an enum field to a file or mapping a POJO to a database table using technologies like Hibernate or JPA.

What exactly is a Java enum constant? A Java enum is a sub-class of java.lang.Enum. An enum constant is simply an object instance of the enum class. The enum constructor must be private. Enum constants are created by the JVM via an invocation of the private constructor. Customizing the constructor signature is the most common way of adding more data to the enum class. You can add methods to the enum class just like any other class.

What does serialization and deserialization of enum mean? Serialization involves converting the enum to a value (usually primitive or a String) which can be easily stored on the disk or a database. Deserialization involves reading a stored value and converting it back to an enum. It is important to understand the different ways in which enum serialization and deserialization works. Sometimes ORM implementations (like Hibernate) may automatically do the enum serialization for you, however there are drawbacks to some of these approaches that you should know.

Let’s say we want to serialize the following enum

public enum Color {
 RED, GREEN, BLUE;
}

Following are 3 different approaches. The last approach is the most reliable way of serializing an enum class, however you should know approaches 1 and 2 so you can understand their drawbacks.

Approach 1: using the ordinal() value

Approach 2: using the name() value

Approach 3: Using a user defined business valueBest practice!

Approach 1: using the ordinal() value

Ordinal of an enum constant is the value is its position in its enum declaration. This is true for all enums. In the above example:

RED.ordinal()   == 0
GREEN.ordinal() == 1
BLUE.ordinal()  == 2

You can serialize the enum by converting the enum to its ordinal() value.

Color color = Color.GREEN;
int ordinal = color.ordinal(); // ordinal == 1
// save ordinal

At a later point, you can deserialize the saved int using the values() method on the enum which returns all the enum constants in the order in this they were declared.

// retrieve from saved value
Color savedColor = Color.values()[savedOrdinal];

This approach is simple and works. Some ORM implementations may automatically use this approach if you map your enum to and Integer column. However, the biggest drawback of this approach is that if new constants are introduced out of order then the serialized values will not be properly reconstructed.

Here’s the error scenario:

  1. Serialize an enum value Color.GREEN. Saved value gets stored as 1
  2. Developer adds a new color ORANGE between RED and GREEN
  3. The previously serialized value of 1 is now mapped to ORANGE.

Approach 2: using the name() value

The name() of any enum is the literal that is used to represent the enum constant in the Java program.

RED.name().equals("RED")
GREEN.name().equals("GREEN")
BLUE.name().equals("BLUE")

Serialize as follows:

Color color = Color.GREEN;
String savedValue = color.name();
// save value

Deserialize as follows:

Color savedColor = Color.valueOf(savedValue);

valueOf() is a built-in method which returns the enum constant with the specified specified name

This method works as well. Some ORM implementations may automatically use this approach if you map your enum to and String (char, varchar) column.The serialized form is String (as opposed to int in the previous example). The main drawback of this approach is that the serialized value, which can then be stored on disk or in database and can be persisted for a long term, is now dependent on a Java constant name. A developer may accidentally rename the constant name without realizing that it will make the previously serialized values unreadable.

Here’s the error scenario:

  1. Serialize an enum value Color.GREEN. Saved value gets stored as “GREEN”
  2. Developer renamed enum constant from Color.GREEN to Color.Green.
  3. The previously serialized value of “GREEN” can no longer be mapped to an existing color.

Approach 3: Using a user defined business value – Recommended approach!

This approach involves assigning a an explicit user defined value to each enum constant and defining a toValue() and fromValue() methods on the enum to do the serialization and deserialization.

public enum Color {
 RED("RED"), GREEN("GREEN"), BLUE("BLUE"), UNKNOWN("UNKNOWN");

 private final String value;

 Color(String value) {
   this.value = value;
 }

 public static Color fromValue(String value) {
   if (value != null) {
     for (Color color : values()) {
       if (color.value.equals(value)) {
         return color;
       }
     }
   }

   // you may return a default value
   return getDefault();
   // or throw an exception
   // throw new IllegalArgumentException("Invalid color: " + value);
 }

 public String toValue() {
   return value;
 }

 public static Color getDefault() {
   return UNKNOWN;
 }
}

This approach is better than approach 1 and approach 2 above. It neither depends on the order in which the enum constants are declared nor on the constant names.

To serialize:

Color color = Color.GREEN;
String savedValue = color.toValue();
// save value

To deserialize:

Color savedColor = Color.fromValue(savedValue);

Note: You can choose an Integer instead of String as a value, but remember this value must not change or you will be unable to retrieve you persisted values.

Mapping enum to database column using JPA/Hibernate

You can use any of the 3 approaches discussed above.

  1. Map the enum to an integer column. The persistence implementation should automatically convert enum to ordinal() and back for you.
  2. Map the enum to a String column. The persistence implementation should automatically convert the enum value to String value via the name() function.
  3. Map the enum using a business value. You should mark the enum field as @Transient, and create another String field which you can map to a String column in your database table. Here’s an example code snippet.
@Entity
public class Product {
 @Column
 private String colorValue;

 @Transient
 public Color getColor() {
  return Color.fromValue(colorValue);
 }

 public void setColor(Color color) {
  this.colorValue = color.toValue();
 }
}

Conclusion

We saw 3 different ways to serialized enums in this article. Approach 1 and 2 above use ordinal() and name() respectively, and require no additional work, but are inherently unsafe to use. The safest way is approach 3 which uses a custom user defined value as shown in the example above. When persisting enums with Hibernate/JPA first convert them to a user defined value.

Related posts:

  1. 2 ways to convert Java Map to String
  2. New Java 7 Feature: String in Switch support
  3. 3 ways to run Java main from Maven

30 comments to 3 ways to serialize Java Enums

  • Aidos

    I have used a variant of option 3 (a derivative of option 2) for hibernate, store the enum class (.getClass()) and the name (.name()) as a string in the database seperated by something you know will never occur (space, colon, etc.) This way you store a fully qualified field name for the enum value that you have stored.

    That way you can write some simple (and generic) reflection code to convert to and from the enum value, returning a default value (or null) if the enum has changed somehow.

  • Joerg Wassmer

    Your approach 3 is pretty error prone. You should use a Hibernate UserType to do the mapping automatically.

  • vineet

    Using name() has the drawback that I mentioned. If the developer renames the enum constant, you won’t be able to retrieve the old value.

  • vineet

    Could you describe or provide a use case how approach 3 is error prone.

  • I concur with Joerg that this is the perfect case for the Hibernate UserType. As soon as someone provides a mutator for the colorValue field, hell will break loose.

    And that can happen if you want hibernate to use accessors for the attributes instead of modifying directly the fields.

  • vineet

    Rafael, can you elaborate why providing a setter (or mutator) method for colorValue field will break the implementation?

  • Jeremy

    Your problem with approach 2 above is that a developer could change the name of the enum, from “GREEN” to “Green” in your example. But surely the same could happen with your preferred “business-value” approach ie. the developer could just as easily change the business-value as he could the enum name?

    While approach 3 may give a level of indirection, I think in the long run having a divergence between the enum name and business-value could just end up being more problematic.

  • vineet

    Jeremy, you have a fair point. I agree that the difference is just one level of indirection, but a very useful one. It moves the persisted value from the “developer space” to “business space”. I think the change in “business-value” will come from business stakeholders and not the developer and that is what makes approach 3 the most robust, at the cost of simplicity. I use approach 3 most of the times, and approach 2 when I am working on a one person project and I know that I will not change the constant name.

    I agree that if theoretically enum constant is dramatically changed, then the divergence between enum name and business-value could be confusing. I just think that it is an uncommon case, the more common case is minor tweaks in the name by the developer, adding/removing underscores, uppercase/lowercase/camelcase modifications.

  • Leo Hart

    We use something similar to example 3 with a Hibernate UserType. Now we have a requirement to create lookups for all our enum values in our database. Not sure if there’s a simple way to do that…

  • Eric Richardson

    What problem are you trying to solve by using different approaches? The loss of a key.
    You are trying to prevent unintended disconnect between a key and a value in an off-line data structure (database table or flat file). None of your solutions do that.

    It is like taking a foreign key, embedding it in a string and dropping the string in a column. Later when you return parse the string, extract the key, lookup the key and find you have broken referential integrity you have no one to blame but yourself.

    If you are using a database, create a legal value table and put a real foreign key constraint. XML with a schema can use an enumeration.

    Use the right tool for the right job.

  • Towid Khan

    your third approach is just a wrapper approach for second approach. Having said that It does isolate the effect from modifying the constant name/order, but however, by mistake a programmer can and will change instantiated value for the constants. I am not sure about Hibernate UseType ( I am not a Hibernate GORU ) but the best approach would be to use the same concept of number 3 , but disable the modification of the key totally. The main idea here is two folded:

    1. Level of indirection
    2. maintain the indirection in case the constants name/order/key changes. Speaking of which, the key must be hidden from the user (e.g. developer ) to prevent it from any diverse effect.

  • vineet

    The user cannot change the “instantiated” value because there is no setter. Moreover, you can define the key as a final to avoid change at runtime.

    I think when you say the user can change the value of the “key”, you probably mean that the user can change the code and modify the constant, right? Yes, that is a concern. However, it less likely for a developer to change a [business] constant without a reason, as opposed to changing the CONSTANT enum value.

  • Towid Khan

    Yup. that is what I meant. While I am on this topic. I have just came up with an idea to make your approach#3 stronger. Why don’t we use a tuple as a key;the String Name along with identifiers to identify the base enum type?

    That is,

    RED(“RED”,Color.Class)
    BLUE(“BLUE”,Color.Class)

    or use random integer in case of class value?

    The motivation behind this is that the developer does not know the exact key and hence cannot change it. of course there is a challenge to map the class type to SQL data type. but Hibernate worry about that :)

  • Maybe it would be the best solution to assign some kind of a enumSerialVersionUID to your enum and store this along with your values. That way, if you modify the constant names (and you know what serialVersionUIDs are) you’d maybe have the old keys persisting in the database, but you can find and transform them. The enumSerialVersionUID has the advantage that it reminds of a standardized behaviour for serializable obejcts, so it’s a little more obvious in contrast to some business values, isn’t it?

  • vineet

    Hi, Bernhard – that would work.

    My point was that the constant literal value (e.g. RED, GREEN) is a value in the developer realm and should not be directly related to values which are persisted. The expectation of developers is that simply renaming a constant like Color.GREEN should not affect old saved data, which I think it is a reasonable expectation. To me that is not too different than someone telling me that I cannot rename a method because that could break previously persisted data.

  • simon

    your third solution, while i see the versatility advantage, is waaaaaay too verbous.

  • SoftwareFads

    Method 3 is the simplest, shortest and easiest way I have seen to persist enums in Hibernate. It is way less verbose than creating a UserType class, which is the “official” Hibernate solution for enums. Hibernate is a very, very verbose system, requiring endless lines of annotations or XML configuration files. Frankly, Hibernate stinks on ice if it is used in any real world project with even a moderate level of complexity.

  • Excellent !!! I was thinking of implementing the same, luckily found the site.

  • Anonymous

    java enums are simply great . I have seen a lot many functionality covered by enum but serialization of enum looks new to me but indeed if you are going to store values in database than this is one of the best way.

  • apalam

    Excellent!!
    You have in fact spelled something that is not spelled anywhere – ORMs by default use name.

    Could you clarify why is the transient annotation on the getter required? My code is working fine without it. JPA with Hibernate
    – Apalam

  • vineet

    When you serialize a bean, serializers typically ignore any fields marked as @Transient. You don’t want to store values for fields which are computed. In this case the idea is to only serialize “colorValue” but not Color which is computed from colorValue.

    Even if you are not serializing, it is a good practice (IMO) to mark computed fields as @Transient. It makes the code very readable, telling the reader at a quick glance which fields hold information and which fields are computed from other fields.

  • James Hogue

    You don’t actually need to mark that as @Transient. You’re not supposed to mix method and field annotations in Hibernate so since you’re not actually saving the Color to a field, you don’t need to mark that method as @Transient, which is probably why it worked for apalam. If you saved Color off to a field, it would make sense to then mark the field as @Transient.

  • DC

    This article is 2 years old, and I can’t believe nobody has pointed this out, but there seems to be a typo in Approach 3 serialize example…

    You have a call to color.value();
    But I think it should be color.toValue();

  • vineet

    Thanks @DC for your feedback, I fixed the typo.

  • tquadrat

    I think you should not talk about “Serialization” but about “Persistence”; “Serialization” would involve Object stream and that a class implements Serializable, but what you describe is persisting objects using ORMs – what usually is something completely different.

    In the context you described your concerns and recommendations are completely valid, but when it comes to real “Serialization” (in opposite to “Persistence”), it is crab.

  • vineet

    You are right @tquadrat. I did mean persistence, not serialization. However, I focused more on serialization because you need to serialize the value somehow before persisting it. I found the challenge is less on the actual persistence, but more on how you serialize it.

  • Mohit Goyal

    I have one doubt.
    enum abc implements Serializable{
    INSTANCE;
    int a;

    private abc() {
    a=10;
    }

    public void setParam(int x){
    a = x;
    }
    }
    public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException
    {
    abc ob = abc.INSTANCE;
    ob.setParam(150);
    File f = new File(“1.txt”);
    FileOutputStream fout = new FileOutputStream(f);
    ObjectOutputStream fob = new ObjectOutputStream(fout);
    fob.writeObject(abc.INSTANCE);
    fob.close();
    FileInputStream fin = new FileInputStream(f);
    ObjectInputStream in = new ObjectInputStream(fin);
    abc ob1 = (abc)(in.readObject());
    ob1.setParam(190);
    }
    both ob and ob1 are same.
    So how enum types are deserialized. Other object types are copied byte by byte dursing deserilization due to this every time u deserialized a new object is created.

    Can u explain me the complete deserilization flow.

  • Eric Shipek

    Another thing to consider with using enum ordinals is when you have larger teams adding new enums to the same types at the same time ;)

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Get Adobe Flash playerPlugin by wpburn.com wordpress themes