SINGLE_TABLE inheritance strategy using enums as discriminator value

前端 未结 7 1957
谎友^
谎友^ 2020-12-15 15:31

Is it possible to use an enum as a discriminator value when using SINGLE_TABLE inheritance strategy?

相关标签:
7条回答
  • 2020-12-15 16:01

    If what you are trying to achieve is to not to duplicate the discriminator values, there is a simple workaround.

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="FREQUENCY",
        discriminatorType=DiscriminatorType.STRING
    )    
    public abstract class Event  {
    }
    
    @Entity
    @DiscriminatorValue(value=Frequency.Values.WEEKLY)
    public class WeeklyEvent extends Event {
        …
    }
    
    public enum Frequency {
        DAILY(Values.DAILY),
        WEEKLY(Values.WEEKLY),
        MONTHLY(Values.MONTHLY);
    
        private String value;
    
        …
    
        public static class Values {
            public static final String DAILY = "D";
            public static final String WEEKLY = "W";
            public static final String MONTHLY = "M";
        }   
    }
    

    Not super elegant, but better than having to maintain the values in multiple places.

    0 讨论(0)
  • 2020-12-15 16:13

    yup ,when you define discriminator the annotation's option are name and discrimatorType

    @DiscriminatorColumn (name="MYDISCRIMINATOR", discriminatorType= DiscriminatorType.INTEGER)
    

    of which DiscriminatorType can only be:

    DiscriminatorType.STRING
    DiscriminatorType.CHAR
    DiscriminatorType.INTEGER
    

    unfortunate I didn't see this yesterday but well. That's the way it is

    0 讨论(0)
  • 2020-12-15 16:14

    I would suggest to invert the relationship: define the discriminator value as a constant in the entity, then wrap it in the enum:

    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(
        name = "FIELD_TYPE",
        discriminatorType = DiscriminatorType.STRING
    )
    public class Shape {}
    
    @Entity
    @DiscriminatorValue(Square.DISCRIMINATOR_VALUE)
    public class Square extends Shape {
        public static final String DISCRIMINATOR_VALUE = "SQUARE";
    }
    
    @Entity
    @DiscriminatorValue(Circle.DISCRIMINATOR_VALUE)
    public class Circle extends Shape {
        public static final String DISCRIMINATOR_VALUE = "CIRCLE";
    }
    
    @AllArgsConstructor
    public enum FieldType {
        SHAPE(Shape.DISCRIMINATOR_VALUE),
        CIRCLE(Circle.DISCRIMINATOR_VALUE);
    
        @Getter
        private final String discriminatorValue;
    }
    

    Apart from eliminating duplication, this code is also less tightly coupled: entity classes don't depend on enum values and can be added without having to change other code; while the enum can "enumerate" different classes from different sources - depending on the context where it is used.

    0 讨论(0)
  • 2020-12-15 16:19

    You can use DiscriminatorType.INTEGER, and map each subclass with @DiscriminatorValue("X"), where X must be the ordinal value of the enum (0,1,2,3...).

    It must be the value as a constant String. You can't use YourEnum.SOME_VALUE.ordinal(), because annotation attribute values must be constants. Yes, it is tedious. Yes, it is error-prone. But it works.

    0 讨论(0)
  • 2020-12-15 16:21

    No, unfortunately you can't.

    If you try to use an enum as discriminator value, you'll get a Type Mismatch exception ("cannot convert from MyEnum to String"), as the only discriminator types allowed are String, Char and Integer. Next, I tried using an enum's name and ordinal combined with DiscriminatorType.STRING and DiscriminatorType.INTEGER, respectively. But this didn't work either, as the @DiscriminatorValue annotation (as any other) requires a constant expression:

    This doesn't work:

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="FREQUENCY",
        discriminatorType=DiscriminatorType.STRING
    )    
    public abstract class Event  {}
    
    @Entity
    @DiscriminatorValue(value=Frequency.WEEKLY.name())
    public class WeeklyEvent extends Event {
        // Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
    }
    

    Doesn't work either:

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="FREQUENCY",
        discriminatorType=DiscriminatorType.INTEGER
    ) 
    public abstract class Event  {}
    
    @Entity
    @DiscriminatorValue(value=Frequency.WEEKLY.ordinal())
    public class WeeklyEvent extends Event {
        // Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
    }
    
    0 讨论(0)
  • 2020-12-15 16:23

    I just wanted to improve the great answer of @asa about the workaround. Usually, we often like to use the discriminator column as an attribute of the abstract class, and mapped with an enum of course. We can still use the solution mentioned above and force some consistencies between enum names (used to map the column) and String values (used as discrimnator values). Here is my suggestion:

    public enum ELanguage {
      JAVA(Values.JAVA), GROOVY(Values.GROOVY);
    
      private ELanguage (String val) {
         // force equality between name of enum instance, and value of constant
         if (!this.name().equals(val))
            throw new IllegalArgumentException("Incorrect use of ELanguage");
      }
    
      public static class Values {
         public static final String JAVA= "JAVA";
         public static final String GROOVY= "GROOVY";
      }
    }
    

    And for the entities, here is the code:

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="LANGUAGE_TYPE", discriminatorType=DiscriminatorType.STRING)    
    public abstract class Snippet {
       // update/insert is managed by discriminator mechanics
       @Column(name = "LANGUAGE_TYPE", nullable = false, insertable = false, updatable = false) 
       @Enumerated(EnumType.STRING)
       public ELanguage languageType
    }
    
    @Entity
    @DiscriminatorValue(value=ELanguage.Values.JAVA)
    public class JavaSnippet extends Snippet {
        …
    }
    

    Still not perfect, but a little bit better, I think.

    0 讨论(0)
提交回复
热议问题