Есть ли способ заставить JPA игнорировать недопустимые типы перечислений?

#java #hibernate #jpa #spring-data-jpa #spring-data

#java #спящий режим #jpa #spring-data-jpa #весна-данные

Вопрос:

Я работаю над микросервисом, который раньше имел один вид перечисления, связанный с одним из его столбцов, но теперь мне пришлось его изменить.

 @Convert(converter = TypeEnumConverter.class)
@Enumerated(EnumType.STRING)
@Column(name = "TIPO", length = 40, nullable = false)
private TypeEnum type;
 

Он отображается таким образом, но имена и типы перечислений несовместимы, есть ли способ сказать JPA игнорировать его, если он не может преобразовать обратно в это перечисление?

Комментарии:

1. что вы подразумеваете под «игнорировать это»? установите type для поля значение null ?

2. ДА. Он пытается преобразовать недопустимый тип (из базы данных, более старый тип перечисления), и это вызывает исключение IllegalArgumentException.

Ответ №1:

Вы можете реализовать пользовательское сопоставление перечислений с помощью AttributeConverter . Затем внутри конвертера вы можете сопоставить неизвестные значения enum с null . Но, пожалуйста, имейте в виду, что следующая операция обновления приведет к удалению значения из этого столбца базы данных.

Вы уже ссылаетесь на свой TypeEnumConverter преобразователь атрибутов в @Convert аннотации в вашем отображении. Если вы хотите его использовать, вам нужно удалить @Enumerated аннотацию. Спецификация JPA не позволяет вам использовать эти 2 аннотации вместе, потому что обе определяют, как сопоставляются значения Enum.

Пример реализации

Вот пример того, как может выглядеть такое сопоставление.

Мое перечисление

Я использую это простое Vehicle перечисление:

 public enum Vehicle {
 
    BUS, CAR, TRAIN, PLANE;
}
 

Мой конвертер атрибутов

Конвертер атрибутов реализует сопоставление между объектом Java и значением, хранящимся в вашей базе данных. Это простой класс Java, который реализует AttributeConverter интерфейс и снабжен @Converter аннотациями . Если вы установите autoApply для атрибута значение true, он будет автоматически использоваться для всех атрибутов сущности преобразованного типа.

 @Converter(autoApply = true)
public class VehicleConverter implements AttributeConverter<Vehicle, String> {
 
    @Override
    public String convertToDatabaseColumn(Vehicle vehicle) {
        switch (vehicle) {
        case Vehicle.BUS:
            return "B";
 
        case Vehicle.CAR:
            return "C";
 
        case Vehicle.TRAIN:
            return "T";
 
        case Vehicle.PLANE:
            return "P";
        }
    }
 
    @Override
    public Vehicle convertToEntityAttribute(String dbData) {
        switch (dbData) {
        case "B":
            return Vehicle.BUS;
 
        case "C":
            return Vehicle.CAR;
 
        case "T":
            return Vehicle.TRAIN;
 
        case "P":
            return Vehicle.PLANE;
 
        default:
            // ignore unknown values
            return null;
        }
    }
 
}
 

Мой класс сущности

Затем вы можете использовать перечисление без каких-либо дополнительных аннотаций сопоставления в вашем классе сущностей.

 @Entity
public class Trip {
     
    private Vehicle vehicle;
 
    ...
}
 

Комментарии:

1. Вы сказали: но, пожалуйста, имейте в виду, что следующая операция обновления затем удалит значение из этого столбца базы данных. Что именно вы имели в виду под этим?

2. Если ваш AttributeConverter устанавливает значение атрибута равным null, а ваш бизнес-код изменяет любой из других атрибутов объекта, Hibernate сгенерирует инструкцию SQL UPDATE и установит значение преобразованного перечисления равным null.

Ответ №2:

Если вы не возражаете потерять устаревшие значения в БД, которые несовместимы с текущим перечислением, тогда ответ @Thorben — это правильный путь.

Если вы хотите сохранить их, то одним из решений было бы объявить ваше поле как строку и предоставить вспомогательные методы для управления значением в виде перечисления. Что-то вроде:

 @Column(name = "TIPO", length = 40, nullable = false)
private String type;

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}

public TypeEnum getTypeEnum() {
    try {
        return TypeEnum.valueOf(type);
    } catch (IllegalArgumentException e) {
        return null;
    }
}
  
public void setTypeEnum(TypeEnum typeEnum) {
    if (typeEnum != null) {
        type = typeEnum.toString();
    }
}