Menelaos
Menelaos

Reputation: 26279

Dealing with changed ENUM definitions - database

Introduction

The lead architect went and changed the ENUM definition in a spring boot project.

From:

public enum ProcessState{
    C("COMPLETE"), P("PARTIAL");
}

To:

public enum ProcessState{
    COMPLETE("COMPLETE"), PARTIAL("PARTIAL");
}

What is the proper way to deal with this? Some other Java Spring Boot applications are now breaking. Would there be a way to tell the jackson deserializer to perform some kind of conversion in these situations?

My Current Work-Around

What I did was to run two update statements on the oracle database:

UPDATE store set PAYLOAD =  REPLACE(PAYLOAD, '"processState":"P"','"processState":"PARTIAL"') where PAYLOAD like '%"processState":"P"%';

UPDATE store set PAYLOAD =  REPLACE(PAYLOAD, '"processState":"C"','"processState":"COMPLETE"') where PAYLOAD like '%"processState":"C"%';

Question

So are there other ways? Could I do it by adding some deserialization/conversion code somewhere for these specific cases? Is there a more elegant way than running a replace SQL statement?

Could I do some kind of hack on a specific java sub-package, and say "use this enum instead of that enum..." or use one of the two? But without affecting the rest of the code?

The error:

java.lang.IllegalArgumentException: No enum constant 

Upvotes: 2

Views: 141

Answers (3)

ChrisGeo
ChrisGeo

Reputation: 3907

Implement a JPA converter like this:

@Converter(autoApply = true)
public class ProcessStateConverter
        implements AttributeConverter<ProcessState, String> {


    private ImmutableBiMap<ProcessState, String> map = ImmutableBiMap.<ProcessState, String>builder()
            .put(COMPLETE, "C")
            .put(COMPRESSING, "P")
        .build();

    @Override
    public String convertToDatabaseColumn(ProcessState attribute) {
        return Optional.ofNullable(map.get(attribute))
                .orElseThrow(() -> new RuntimeException("Unknown ProcessState: " + attribute));
    }

    @Override
    public ProcessState convertToEntityAttribute(String dbData) {
        return Optional.ofNullable(map.inverse().get(dbData))
                .orElseThrow(() -> new RuntimeException("Unknown String: " + dbData));
    }
}

Remember to treat your Enum like a simple column and not @Enumerated i.e.

@Entity
public class MyEntity {

    @Column //no @Enumerated
    private ProcessState processState; 

    //...
}

The drawback is that you need to maintain the converter each time something changes. So better create a unit test to check if everything is correctly mapped.

Upvotes: 1

Menelaos
Menelaos

Reputation: 26279

One additional solution to the others posted:

   @JsonCreator
        public static ProcessState factory(String inputValue) {

        if(inputValue.length() == 1){

            for(ProcessState type : ProcessState.values()){
                if(inputValue.equals(type.getValue().substring(0,inputValue.length()))){
                    return type;
                }
            }
        }


        return ProcessState .valueOf(inputValue);
}

Upvotes: 1

Yogi
Yogi

Reputation: 1895

Ideally we store value of emum rather than Enum.
So, you should save ENUM values like COMPLETE,PARTIAL
For JSON serialization and de-serialization, use @JsonValue

@JsonValue
public String toValue() {
   return value;
}

Upvotes: 1

Related Questions