Is it possible to write a generic enum converter for JPA?
Based on @scottb solution I made this, tested against hibernate 4.3: (no hibernate classes, should run on JPA just fine)
Interface enum must implement:
public interface PersistableEnum<T> {
public T getValue();
}
Base abstract converter:
@Converter
public abstract class AbstractEnumConverter<T extends Enum<T> & PersistableEnum<E>, E> implements AttributeConverter<T, E> {
private final Class<T> clazz;
public AbstractEnumConverter(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public E convertToDatabaseColumn(T attribute) {
return attribute != null ? attribute.getValue() : null;
}
@Override
public T convertToEntityAttribute(E dbData) {
T[] enums = clazz.getEnumConstants();
for (T e : enums) {
if (e.getValue().equals(dbData)) {
return e;
}
}
throw new UnsupportedOperationException();
}
}
You must create a converter class for each enum, I find it easier to create static class inside the enum: (jpa/hibernate could just provide the interface for the enum, oh well...)
public enum IndOrientation implements PersistableEnum<String> {
LANDSCAPE("L"), PORTRAIT("P");
private final String value;
@Override
public String getValue() {
return value;
}
private IndOrientation(String value) {
this.value= value;
}
public static class Converter extends AbstractEnumConverter<IndOrientation, String> {
public Converter() {
super(IndOrientation.class);
}
}
}
And mapping example with annotation:
...
@Convert(converter = IndOrientation.Converter.class)
private IndOrientation indOrientation;
...
With some changes you can create a IntegerEnum interface and generify for that.
What you need to do is write a generic base class and then extend that for each enum type you want to persist. Then use the extended type in the @Converter
annotation:
public abstract class GenericEnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {
...
}
public FooConverter
extends GenericEnumUppercaseConverter<Foo>
implements AttributeConverter<Foo, String> // See Bug HHH-8854
{
public FooConverter() {
super(Foo.class);
}
}
where Foo
is the enum you want to handle.
The alternative would be to define a custom annotation, patch the JPA provider to recognize this annotation. That way, you could examine the field type as you build the mapping information and feed the necessary enum type into a purely generic converter.
Related:
- https://hibernate.atlassian.net/browse/HHH-8854