Converting Java enums to values and back

You know the stuff, you have some enums that logically wraps for instance integer constants. Typical case is numeric DB column mapped to enum. We all know that using EnumType.ORDINAL is one of those worse solutions. And EnumType.STRING is not really 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) which would be Integer in JPA entity.

This mapping does not necessarily related only to JPA, there are many cases when you want to convert enum to something and back. For instance, with generated web-services I often map generated enum to my “business” one and if it suits me (as it is not always clean) I embed this mapping into my enum (not the generated one of course :-)). So I map enum to another related enum and back. But we will work with JPA example. JPA 2.1 with AttributeConverter<X,Y> to be precise. You can do something similar with Hibernate converters too, I’m sure.

This blog post is accompanied by this GitHub project containing all the variations. Copy and paste and use whatever however you like.

Naive approach

Before going on, know that you can check all examples in this GitHub repository. First case can be found in this location. We have our simple enum:

public enum SomeEntityType {
	NORMAL,
	SPECIAL
}

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

@Entity
public class SomeEntity {
	@Id private Integer id;

	@Convert(converter = SomeEntityTypeConverter.class)
	@Column(name = "type")
	private SomeEntityType type;
}

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 finally allows us to do conversion (JPA 2.1, part of Java EE 7). All we have to do now is to implement that converter:

import javax.persistence.AttributeConverter;

/**
 * This is coupled too much to enum and you always have to change both classes in tandem.
 * That's a big STOP (and think) sign in any case.
 */
public class SomeEntityTypeConverter implements AttributeConverter<SomeEntityType, Integer> {
	@Override
	public Integer convertToDatabaseColumn(SomeEntityType someEntityType) {
		switch (someEntityType) {
			case NORMAL: return 0;
			case SPECIAL: return 1;
			default:
				// do we need this? if for nothing else it catches forgotten case when enum is modified
				throw new IllegalArgumentException("Invalid value " + someEntityType);
				// actually the value is valid, just this externalized switch sucks of course
		}
	}

	@Override
	public SomeEntityType convertToEntityAttribute(Integer dbValue) {
		switch (dbValue) {
			case 0: return SomeEntityType.NORMAL;
			case 1: return SomeEntityType.SPECIAL;
		}
		// now what? probably exception would be better just to warn programmer
		// but if it happens in production, it doesn't really matter if it's here or NPE later
		return null;
	}
}

Ok, so I revealed the problems in the comments. There may be just one reason to do it this way. If you need the enum independent from that dbValue mapping – but I really don’t know why. On the contrary, you should go for cohesive solution. And in this case you will be just fine with encapsulation. Put the stuff that changes into one place – the enum.

Encapsulated conversion

You need to implement AttributeConverter, because it is kind of glue between JPA and your 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 I want to see looks like this:

public class SomeEntityTypeConverter implements AttributeConverter<SomeEntityType, Integer> {
	@Override
	public Integer convertToDatabaseColumn(SomeEntityType someEntityType) {
		return someEntityType.getDbValue();
	}

	@Override
	public SomeEntityType convertToEntityAttribute(Integer dbValue) {
		// this can still return null unless it throws IllegalArgumentException
		// which would be in line with enums static valueOf method
		return SomeEntityType.fromDbValue(dbValue);
	}
}

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:

public enum SomeEntityType {
	NORMAL(0),
	SPECIAL(1);

	// ENUM -> DB VALUE CONVERSION
	// first part is easy and most programmers get here (Effective Java 101 after all)
	// fields are not final implicitly, but we better make them
	private final Integer dbValue;

	private SomeEntityType(Integer dbValue) {
		this.dbValue = dbValue;
	}

	public Integer getDbValue() {
		return dbValue;
	}

	// DB VALUE -> ENUM CONVERSION
	// static reverse resolving:
	public static final Map<Integer, SomeEntityType> dbValues = new HashMap<>();

	static {
		for (SomeEntityType value : values()) {
			dbValues.put(value.dbValue, value);
		}
	}

	public static SomeEntityType fromDbValue(Integer dbValue) {
		// this returns null for invalid value, check for null and throw exception if you need it
		return dbValues.get(dbValue);
	}
}

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. You can find this solution here on GitHub.

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 microframework

Let’s say we want to have order in these things. We will require the method named toDbValue, etc. So our enums will implement interface ConvertedEnum:

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

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 IAE, but I’ll show how we can use null in conversion.

What are the changes in the enum? Minimal really, just add implements ConvertedEnum<Integer> and you can add @Override over toDbValue method. Not worth the listing as it’s all here on GitHub anyway.

Now to utilize all this we need a base class for AttributeConverter – here it goes:

/**
 * Base implementation for converting enums stored in DB.
 * Enums must implement {@link ConvertedEnum}.
 */
public abstract class EnumAttributeConverter<X extends ConvertedEnum<Y>, Y>
	implements AttributeConverter<X, Y>
{
	@Override
	public final Y convertToDatabaseColumn(X x) {
		return x.toDbValue();
	}

// you can end here, or you can add handy stuff if necessary
	public X notNull(X x) {
		return notNull(x, null);
	}

	public X notNull(X x, Y dbValue) {
		if (x == null) {
			throw new IllegalArgumentException("No enum constant" + (dbValue != null ? (" for DB value " + dbValue) : ""));
		}
		return x;
	}

	public X withDefault(X x, X defaultValue) {
		return x != null ? x : defaultValue;
	}
}

As you can see, only convertToDatabaseColumn is mandatory here. Here is our concrete converter:

public class SomeEntityTypeConverter extends EnumAttributeConverter<SomeEntityType, Integer> {
	@Override
	public SomeEntityType convertToEntityAttribute(Integer integer) {
		return notNull(SomeEntityType.fromDbValue(integer));
	}
}

What a beauty suddenly! I also used notNull to enforce IllegalArgumentException (unless you do it on enum level), or I could use withDefault to fallback to some default value – if it makes sense of course (better when it doesn’t :-)).

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? It is static, so it seems problematic – 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:

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

And subclass would initialize it this way:

public class SomeEntityTypeConverter extends EnumAttributeConverter<SomeEntityType, Integer> {
	{
		enumClass = SomeEntityType.class;
	}

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

	protected abstract Class<X> enumClass();

And in concrete class:

public class SomeEntityTypeConverter extends EnumAttributeConverter<SomeEntityType, Integer> {
	@Override
	protected Class<SomeEntityType> enumClass() {
		return SomeEntityType.class;
	}
}

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

	@Override
	public X convertToEntityAttribute(Y dbValue) {
		try {
			Method method = enumClass().getMethod("fromDbValue", dbValue.getClass());
			return (X) method.invoke(null, dbValue);
		} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
			throw new IllegalArgumentException("...this really doesn't make sense", e);
		}
	}

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 is 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:

// throws NoSuchMethodException
// Method method = converter.getClass().getMethod("convertToDatabaseColumn", Integer.class);
Method method = converter.getClass().getMethod("convertToDatabaseColumn", Object.class);
System.out.println("method = " + method);

// prints:
method = public java.lang.Object com.github.virgo47.enumconv._4evenmore.EnumAttributeConverter.convertToDatabaseColumn(java.lang.Object)

Great… so if something strictly matches the method types with column/enum types, we may have a misunderstanding. Sometimes too smart may be just that – too smart. Check this overboard solution here on GitHub.

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:

	// static resolving:
	public static final ConvertedEnumResolver<SomeEntityType, Integer> resolver = new ConvertedEnumResolver<>(SomeEntityType.class);

	public static SomeEntityType fromDbValue(Integer dbValue) {
		return resolver.get(dbValue);
	}

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

/**
 * Helps reverse resolving of {@link ConvertedEnum} from a DB value back to enum instance.
 * Enums that can be resolved this way must have unified interface in order to obtain
 * {@link ConvertedEnum#toDbValue()}.
 *
 * @param <T> type of an enum
 * @param <Y> type of DB value
 */
public class ConvertedEnumResolver<T extends ConvertedEnum<Y>, Y> {

	private final String classCanonicalName;
	private final Map<Y, T> dbValues = new HashMap<>();

	public ConvertedEnumResolver(Class<T> enumClass) {
		classCanonicalName = enumClass.getCanonicalName();
		for (T t : enumClass.getEnumConstants()) {
			dbValues.put(t.toDbValue(), t);
		}
	}

	public T get(Y dbValue) {
		T enumValue = dbValues.get(dbValue);
		if (enumValue == null) {
			throw new IllegalArgumentException("No enum constant for dbValue " + dbValue + " in " + classCanonicalName);
		}
		return enumValue;
	}
}

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. Check the complete sources here on GitHub.

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 – maybe the most questionable part here. It takes care of one direction of the conversion, just be aware of potential problems if something introspect the method types.

Alternative conversions with entities

While not exactly on the topic of enum to value mapping (and back), we were demonstrating this all in context of JPA 2.1. So I’d like to at least mention alternative solutions for older JPA versions.

  • You can always use vendor specific extensions. Hibernate has its Custom types and I’m sure EclipseLink doesn’t fall behind.
  • But there is also possibility to use mapping annotations on properties. That is put your @Id on the getter, or specify AccessType.PROPERTY. This allows you to convert anything to anything in getter/setter. I even used it to back Dates by long (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. Hibernate used get/set for Date, and I had @Transient get/set for millis long available. I actually liked comparing millis more than 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.

I hope you liked our tour around enums. If you knew or used this before, good for you. I’m surprised how many programmers don’t try to design and refactor these little things where you don’t even need to think in UML.

And if you have better solutions, share them in comments, please. 🙂

Advertisements

About virgo47
Java Developer by profession in the first place. Gamer and amateur musician. And father too. Naive believer in brighter future. Step by step.

3 Responses to Converting Java enums to values and back

  1. Pingback: Converting Java enums to values and back – with Java 8! | Virgo's Naive Stories

  2. Amar Panigrahy says:

    Thank you, was searching for this kind of comprehensive article 🙂

  3. Pingback: Opinionated JPA with Querydsl book finished! | Virgo's Naive Stories

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s