This answer has been modified to take advantage of default
interface methods in Java 8.
The number of components of the facility (enumerated below) remains at four, but the amount of required boilerplate is much less. The erstwhile AbstractEnumConverter
class has been replaced by an interface named JpaEnumConverter
which now extends the JPA AttributeConverter
interface. Moreover, each placeholder JPA @Converter
class now only requires the implementation of a single abstract method that returns the Class<E>
object for the enum (for even less boilerplate).
This solution is similar to others and also makes use of the JPA Converter facility introduced in JPA 2.1. As generic types in Java 8 are not reified, there does not appear to be an easy way to avoid writing a separate placeholder 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:
Encodable
interface; the contract for an enum class that grants access to a String
token for each enum constant. This is written only once and is implemented by all enum classes that are to be persisted via JPA. This interface also contains a static factory method for getting back the enum constant for its matching token.
JpaEnumConverter
interface; provides the common code for translating tokens to/from enum constants. This is also only written once and is implemented by all the placeholder @Converter
classes in the project.
- Each Java enum class in the project implements the
Encodable
interface.
- Each JPA placeholder @Converter class implements the
JpaEnumConverter
interface.
The Encodable
interface is simple and contains a static factory method, forToken()
, for obtaining enum constants:
public interface Encodable{
String token();
public static <E extends Enum<E> & Encodable> E forToken(Class<E> cls, String tok) {
final String t = tok.trim();
return Stream.of(cls.getEnumConstants())
.filter(e -> e.token().equalsIgnoreCase(t))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unknown token '" +
tok + "' for enum " + cls.getName()));
}
}
The JpaEnumConverter
interface is a generic interface that is also simple. It extends the JPA 2.1 AttributeConverter
interface and implements its methods for translating back and forth between entity and database. These are then inherited by each of the JPA @Converter classes. The only abstract method that each placeholder class must implement, is the one that returns the Class<E>
object for the enum.
public interface JpaEnumConverter<E extends Enum<E> & Encodable>
extends AttributeConverter<E, String> {
public abstract Class<E> getEnumClass();
@Override
public default String convertToDatabaseColumn(E attribute) {
return (attribute == null)
? null
: attribute.token();
}
@Override
public default E convertToEntityAttribute(String dbData) {
return (dbData == null)
? null
: Encodeable.forToken(getEnumClass(), dbData);
}
}
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 Encodable
, and that the token for each enum constant is defined as a private field):
public enum GenderCode implements Encodable{
MALE ("M"),
FEMALE ("F"),
OTHER ("O");
final String e_token;
GenderCode(String v) {
this.e_token = v;
}
@Override
public String token() { // the only abstract method of Encodable
return this.e_token;
}
}
The boilerplate for every placeholder JPA 2.1 @Converter
class would now look like the code below. Note that every such converter will need to implement JpaEnumConverter
and provide the implementation for getEnumClass()
... and that's all! The implementations for the JPA AttributeConverter
interface methods are inherited.
@Converter
public class GenderCodeConverter
implements JpaEnumConverter<GenderCode> {
@Override
public Class<GenderCode> getEnumClass() { // sole abstract method
return GenderCode.class;
}
}
These placeholder @Converter
classes can be readily nested as static
member classes of their associated enum classes.