Java JPA/Hibernate-将枚举持久化为常量表

Java JPA/Hibernate-将枚举持久化为常量表,java,hibernate,jpa,enums,Java,Hibernate,Jpa,Enums,目前,我正在使用标准方法将枚举映射到Hibernate,例如 @Entity public class Job { @Enumerated(EnumType.STRING) protected State state; } public enum State{ NEW,OLD; } 现在需求发生了变化,我必须创建一个表State,其中包含作为字符串常量的枚举的所有有效值。因此作业必须参考状态表。我不必迁移遗留数据 我必须用JPA/Hibernate映射哪些选项 是否可

目前,我正在使用标准方法将枚举映射到Hibernate,例如

@Entity
public class Job {
    @Enumerated(EnumType.STRING)
    protected State state;
}

public enum State{
    NEW,OLD;
}
现在需求发生了变化,我必须创建一个表
State
,其中包含作为字符串常量的枚举的所有有效值。因此
作业
必须参考
状态
表。我不必迁移遗留数据

  • 我必须用JPA/Hibernate映射哪些选项
  • 是否可以让Hibernate使用值创建
    状态
    (1->“新建”,2->“旧”)。
    在DDL生成期间

  • 我觉得这没什么道理。
    enum
    的值是静态和常量,而SQL表中的值是动态的。如果DB不包含此类枚举的确切值(不多也不少),该怎么办

    编辑:如果你被迫实施它,像这样的事情可能会奏效吗

    public enum State{
        int primaryKey;
        NEW(0),OLD(1);
        public State(int pk) {
          primarykey = pk;
        }
    }
    

    然后通过主键联接….

    最终得到一个解决方案,该解决方案生成DDL,包括枚举常量和外键约束

    e、 g

    使用以下
    MetadataContributor
    (/src/main/resources/META-INF/services/org.hibernate.boot.spi.MetadataContributor)就足够了:

    公共类EnumConstantsMetadataContributor实现MetadataContributor{
    私有最终静态记录器日志=LoggerFactory.getLogger(EnumConstantMetadataContributor.class);
    私有最终静态列表启用_ON=Arrays.asList(“验证”、“更新”、“创建”、“创建删除”);
    私有最终静态整数默认值\u VARCHAR\u SIZE=255;
    私有最终静态标识符默认\列\名称=Identifier.toIdentifier(“枚举\常量”,false);
    @凌驾
    public void贡献(InFlightMetadataCollector metadataCollector,IndexView-jandexIndex){
    if(应该运行(metadataCollector)){
    AddenumStableConsTantsAndFkConstraint(metadataCollector);
    }
    }
    私有布尔值应运行(InFlightMetadataCollector metadataCollector){
    StandardServiceRegistry serviceRegistry=metadataCollector.getMetadataBuildingOptions().getServiceRegistry();
    ConfigurationService config=serviceRegistry.getService(ConfigurationService.class);
    String setting=config.getSetting(AvailableSettings.HBM2DDL_AUTO,String.class,null);
    返回(设置!=null | |启用_ON.contains(设置));
    }
    私有void AddenumStableConsTantsAndFkConstraint(InFlightMetadataCollector metadataCollector){
    for(PersistentClass PersistentClass:metadataCollector.getEntityBindings()){
    类plainJavaClass=persistentClass.getMappedClass();
    if(Enum.class.isAssignableFrom((plainJavaClass))){
    createEnumInsertsAndDbColumns(persistentClass、plainJavaClass、metadataCollector);
    }
    tryAddFkConstraint(persistentClass,metadataCollector);
    }
    }
    私有void tryAddFkConstraint(PersistentClass PersistentClass,InFlightMetadataCollector metadataCollector){
    消费者createEnumFkConstraintForField=字段->{
    字符串fieldName=field.getName();
    PersistentClass targetPersistentClass=metadataCollector.getEntityBinding(field.getType().getCanonicalName());
    if(targetPersistentClass==null){
    error(“Target(enum)类必须是@Entity:{}”,field.getType().getCanonicalName());
    系统出口(1);
    }
    属性enumReferenceAnnotatedProperty=persistentClass.getProperty(字段名);
    persistentClass.getTable().createForeignKey(null,
    Arrays.asList(enumReferenceAnnotatedProperty.getColumnIterator().next()),
    targetPersistentClass.getEntityName());
    };
    Field[]declaredFields=persistentClass.getMappedClass().getDeclaredFields();
    of(declaredFields).filter(field->field.isAnnotationPresent(EnumReference.class)).forEach(
    createEnumFkConstraintForField);
    }
    私有void createEnumInsertsAndDbColumns(PersistentClass PersistentClass、Class clazz、,
    InFlightMetadataCollector元数据){
    字符串tableName=persistentClass.getTable().getName();
    Enum[]enumJavaConstants=clazz.asSubclass(Enum.class).getEnumConstants();
    ArrayList insertCommandAccumulator=新的ArrayList(enumJavaConstants.length);
    可选enumValueAnnotatedField=of(enumJavaConstants.getClass().getComponentType().getDeclaredFields())
    .filter(field->field.isAnnotationPresent(EnumValue.class)).map(fieldWithEnumValue->{
    fieldWithEnumValue.setAccessible(true);
    返回带有枚举值的字段;
    }).findAny();//只支持一个或一个
    if(enumValueAnnotatedField.isPresent()){
    SetMinimalFieldLengthoExitingColumn(enumValueAnnotatedField.get(),enumJavaConstants,persistentClass);
    }
    for(int i=0;igetInstanceValueOfEnumValueAnnotation(it,v))
    .orElse(it.name());
    如果(!enumValueAnnotatedField.isPresent()){
    insertAdditionalColumn(persistentClass、metadata.getDatabase()、enumJavaConstants);
    }
    add(createInsert(tableName,i,constantEnumValue));
    }
    InitCommand InitCommand=newinitcommand(insertCommandAccumulator.toArray(新字符串[0]);
    persistentClass.getTable().addInitCommand(initCommand);
    }
    私有void setMinimalFieldLengthOfExitingColumn(字段字段,枚举[]enumJavaConstants,
    PersistentClass(PersistentClass){
    Property=persistentClass.getProperty(field.getName());
    Column Column=persistentClass.getTable().getColumn(标识符.t
    
    @Entity
    public enum MyEnum{
        @EnumValue
        private String name;
        @Id
        private int id;
    }
    @Entity
    public class MyEntity {
        @EnumReference
        protected MyEnum myEnum;
    }
    
    public class EnumConstantsMetadataContributor implements MetadataContributor {
        private final static Logger LOG = LoggerFactory.getLogger(EnumConstantsMetadataContributor.class);
    
        private final static List<String> ENABLED_ON = Arrays.asList("validate", "update", "create", "create-drop");
        private final static Integer DEFAULT_VARCHAR_SIZE = 255;
        private final static Identifier DEFAULT_COLUMN_NAME = Identifier.toIdentifier("enum_constant", false);
    
        @Override
        public void contribute(InFlightMetadataCollector metadataCollector, IndexView jandexIndex) {
            if (shouldRun(metadataCollector)) {
                addEnumsAsTableConstantsAndFkConstraint(metadataCollector);
            }
        }
    
        private boolean shouldRun(InFlightMetadataCollector metadataCollector) {
            StandardServiceRegistry serviceRegistry = metadataCollector.getMetadataBuildingOptions().getServiceRegistry();
            ConfigurationService config = serviceRegistry.getService(ConfigurationService.class);
            String setting = config.getSetting(AvailableSettings.HBM2DDL_AUTO, String.class, null);
            return (setting != null || ENABLED_ON.contains(setting));
        }
    
        private void addEnumsAsTableConstantsAndFkConstraint(InFlightMetadataCollector metadataCollector) {
            for (PersistentClass persistentClass : metadataCollector.getEntityBindings()) {
                Class<?> plainJavaClass = persistentClass.getMappedClass();
                if (Enum.class.isAssignableFrom((plainJavaClass))) {
                    createEnumInsertsAndDbColumns(persistentClass, plainJavaClass, metadataCollector);
                }
                tryAddFkConstraint(persistentClass, metadataCollector);
            }
        }
    
        private void tryAddFkConstraint(PersistentClass persistentClass, InFlightMetadataCollector metadataCollector) {
            Consumer<Field> createEnumFkConstraintForField = field -> {
                String fieldName = field.getName();
                PersistentClass targetPersistentClass = metadataCollector.getEntityBinding(field.getType().getCanonicalName());
                if (targetPersistentClass == null) {
                    LOG.error("Target (enum) class must be an @Entity: {}", field.getType().getCanonicalName());
                    System.exit(1);
                }
                Property enumReferenceAnnotatedProperty = persistentClass.getProperty(fieldName);
                persistentClass.getTable().createForeignKey(null,
                        Arrays.asList(enumReferenceAnnotatedProperty.getColumnIterator().next()),
                        targetPersistentClass.getEntityName());
            };
    
            Field[] declaredFields = persistentClass.getMappedClass().getDeclaredFields();
            of(declaredFields).filter(field -> field.isAnnotationPresent(EnumReference.class)).forEach(
                    createEnumFkConstraintForField);
        }
    
        private void createEnumInsertsAndDbColumns(PersistentClass persistentClass, Class<?> clazz,
                InFlightMetadataCollector metadata) {
            String tableName = persistentClass.getTable().getName();
            Enum<?>[] enumJavaConstants = clazz.asSubclass(Enum.class).getEnumConstants();
    
            ArrayList<String> insertCommandAccumulator = new ArrayList<String>(enumJavaConstants.length);
    
            Optional<Field> enumValueAnnotatedField = of(enumJavaConstants.getClass().getComponentType().getDeclaredFields())
                    .filter(field -> field.isAnnotationPresent(EnumValue.class)).map(fieldWithEnumValue -> {
                        fieldWithEnumValue.setAccessible(true);
                        return fieldWithEnumValue;
                    }).findAny(); // just none or one is supported
    
            if (enumValueAnnotatedField.isPresent()) {
                setMinimalFieldLengthOfExitingColumn(enumValueAnnotatedField.get(), enumJavaConstants, persistentClass);
            }
    
            for (int i = 0; i < enumJavaConstants.length; i++) {
                Enum<?> it = enumJavaConstants[i];
                String constantEnumValue = enumValueAnnotatedField.map(v -> getInstanceValueOfEnumValueAnnotation(it, v))
                        .orElse(it.name());
                if (!enumValueAnnotatedField.isPresent()) {
                    insertAdditionalColumn(persistentClass, metadata.getDatabase(), enumJavaConstants);
                }
                insertCommandAccumulator.add(createInsert(tableName, i, constantEnumValue));
            }
    
            InitCommand initCommand = new InitCommand(insertCommandAccumulator.toArray(new String[0]));
            persistentClass.getTable().addInitCommand(initCommand);
        }
    
        private void setMinimalFieldLengthOfExitingColumn(Field field, Enum<?>[] enumJavaConstants,
                PersistentClass persistentClass) {
            Property property = persistentClass.getProperty(field.getName());
            Column column = persistentClass.getTable().getColumn(Identifier.toIdentifier(property.getName()));
            Integer maxLengthOfEnums = maxLengthOfEnums(enumJavaConstants,
                    e -> getInstanceValueOfEnumValueAnnotation(e, field));
            column.setLength(maxLengthOfEnums);
        }
    
        private String getInstanceValueOfEnumValueAnnotation(Enum<?> myEnum, Field enumValueAnnotatedField) {
            try {
                return enumValueAnnotatedField.get(myEnum).toString();
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
                return null;
            }
        }
    
        private static Integer maxLengthOfEnums(Enum<?>[] enums, Function<Enum<?>, String> enumConstantValueExtractor) {
            return of(enums).map(it -> enumConstantValueExtractor.apply(it).length()).reduce(Math::max)
                    .orElse(DEFAULT_VARCHAR_SIZE);
        };
    
        private void insertAdditionalColumn(PersistentClass persistentClass, Database database, Enum<?>[] enumJavaConstants) {
            Integer maxEnumStringLength = maxLengthOfEnums(enumJavaConstants, c -> c.name());
            Column column = new Column(DEFAULT_COLUMN_NAME.render(database.getDialect()));
            String typeName = database.getDialect().getTypeName(Types.VARCHAR, maxEnumStringLength, 0, 0);
            column.setSqlType(typeName);
            persistentClass.getTable().addColumn(column);
        }
    
        private String createInsert(String tableName, int position, String dbEnumValue) {
            return ("insert into " + tableName + " values(" + position + ",\'" + dbEnumValue + "\')");
        }
    }