I wanted to write a Converter for JPA that stores any enum as UPPERCASE. Some enums we encounter do not follow yet the convention to use only Uppercase letters so until they
My solution to this problem looks similar and also makes use of the JPA 2.1 Converter facility. Alas, generic types in Java 8 are not reified, and so there does not appear to be an easy way to avoid writing a separate class for each Java enum that you want to be able to convert to/from a database format.
You can however reduce the process of writing an enum converter class to pure boilerplate. The components of this solution are:
Encodeable interface; the contract for an enum class that grants access to a String token for each enum constant (also a factory for getting the enum constant for a matching token)AbstractEnumConverter class; provides the common code for translating tokens to/from enum constantsEncodeable interfaceAbstractEnumConverter classThe Encodeable interface is simple and contains a static factory method, forToken(), for obtaining enum constants:
public interface Encodeable {
String token();
public static & Encodeable> E forToken(Class cls, String tok) {
final String t = tok.trim().toUpperCase();
return Stream.of(cls.getEnumConstants())
.filter(e -> e.token().equals(t))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown token '" +
tok + "' for enum " + cls.getName()));
}
}
The AbstractEnumConverter class is a generic class that is also simple. It implements the JPA 2.1 AttributeConverter interface but provides no implementations for its methods (because this class can't know the concrete types needed for obtaining the appropriate enum constants). Instead, it defines helper methods that the concrete converter classes will chain to:
public abstract class AbstractEnumConverter & Encodeable>
implements AttributeConverter {
public String toDatabaseColumn(E attr) {
return (attr == null)
? null
: attr.token();
}
public E toEntityAttribute(Class cls, String dbCol) {
return (dbCol == null)
? null
: Encodeable.forToken(cls, dbCol);
}
}
An example of a concrete enum class that could now be persisted to a database with the JPA 2.1 Converter facility is shown below (note that it implements Encodeable, and that the token for each enum constant is defined as a private field):
public enum GenderCode implements Encodeable {
MALE ("M"),
FEMALE ("F"),
OTHER ("O");
final String e_token;
GenderCode(String v) {
this.e_token = v;
}
@Override
public String token() {
return this.e_token;
}
}
The boilerplate for every JPA 2.1 Converter class would now look like this (note that every such converter will need to extend AbstractEnumConverter and provide implementations for the methods of the JPA AttributeConverter interface):
@Converter
public class GenderCodeConverter
extends AbstractEnumConverter {
@Override
public String convertToDatabaseColumn(GenderCode attribute) {
return this.toDatabaseColumn(attribute);
}
@Override
public GenderCode convertToEntityAttribute(String dbData) {
return this.toEntityAttribute(GenderCode.class, dbData);
}
}