Is it possible to write a generic enum converter for JPA?

后端 未结 4 1742
萌比男神i
萌比男神i 2020-12-01 04:51

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

4条回答
  •  没有蜡笔的小新
    2020-12-01 05:14

    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:

    1. 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)
    2. AbstractEnumConverter class; provides the common code for translating tokens to/from enum constants
    3. Java enum classes that implement the Encodeable interface
    4. JPA converter classes that extend the AbstractEnumConverter class

    The 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);
        }
    }
    

提交回复
热议问题