Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用固定值映射JPA中的枚举?_Java_Spring_Orm_Jpa_Enums - Fatal编程技术网

Java 使用固定值映射JPA中的枚举?

Java 使用固定值映射JPA中的枚举?,java,spring,orm,jpa,enums,Java,Spring,Orm,Jpa,Enums,我正在寻找使用JPA映射枚举的不同方法。我特别想设置每个枚举项的整数值,并且只保存整数值 @Entity @Table(name = "AUTHORITY_") public class Authority implements Serializable { public enum Right { READ(100), WRITE(200), EDITOR (300); private int value; Right(int value) { th

我正在寻找使用JPA映射枚举的不同方法。我特别想设置每个枚举项的整数值,并且只保存整数值

@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {

  public enum Right {
      READ(100), WRITE(200), EDITOR (300);

      private int value;

      Right(int value) { this.value = value; }

      public int getValue() { return value; }
  };

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "AUTHORITY_ID")
  private Long id;

  // the enum to map : 
  private Right right;
}
一个简单的解决方案是将枚举注释与EnumType.ORDINAL一起使用:

@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
但在本例中,JPA映射的是枚举索引(0,1,2),而不是我想要的值(100200300)

我发现的两个解决方案似乎并不简单

第一种解决方案 解决方案使用@PrePersist和@PostLoad将枚举转换为其他字段,并将枚举字段标记为瞬态:

@Basic
private int intValueForAnEnum;

@PrePersist
void populateDBFields() {
  intValueForAnEnum = right.getValue();
}

@PostLoad
void populateTransientFields() {
  right = Right.valueOf(intValueForAnEnum);
}
第二种解决方案 第二个解决方案提出了一个通用的转换对象,但看起来仍然很重并且面向hibernate(Java EE中似乎不存在@Type):

还有其他解决办法吗? 我有几个想法,但我不知道JPA中是否有:

  • 加载和保存Authority对象时,请使用Authority类的右成员的setter和getter方法
  • 一个等价的想法是告诉JPA什么是正确的enum方法来将enum转换为int和int转换为enum
  • 因为我使用的是Spring,有没有办法告诉JPA使用特定的转换器(RightEditor)

对于JPA2.1之前的版本,JPA只提供了两种处理枚举的方法,一种是通过枚举的
名称,另一种是通过枚举的
序号。而且标准JPA不支持自定义类型。因此:

  • 如果要进行自定义类型转换,必须使用提供程序扩展(使用Hibernate
    UserType
    、EclipseLink
    Converter
    等)。(第二种解决办法)~或~
  • 您必须使用@PrePersist和@PostLoad技巧(第一种解决方案)~或~
  • 注释getter和setter获取并返回
    int
    value~或~
  • 在实体级别使用整数属性,并在getter和setter中执行转换
我将说明最新的选项(这是一个基本的实现,根据需要进行调整):


可能是Pascal的密切相关代码

@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {

    public enum Right {
        READ(100), WRITE(200), EDITOR(300);

        private Integer value;

        private Right(Integer value) {
            this.value = value;
        }

        // Reverse lookup Right for getting a Key from it's values
        private static final Map<Integer, Right> lookup = new HashMap<Integer, Right>();
        static {
            for (Right item : Right.values())
                lookup.put(item.getValue(), item);
        }

        public Integer getValue() {
            return value;
        }

        public static Right getKey(Integer value) {
            return lookup.get(value);
        }

    };

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "AUTHORITY_ID")
    private Long id;

    @Column(name = "RIGHT_ID")
    private Integer rightId;

    public Right getRight() {
        return Right.getKey(this.rightId);
    }

    public void setRight(Right right) {
        this.rightId = right.getValue();
    }

}
@实体
@表(name=“AUTHORITY\”)
公共类权限实现了可序列化{
公共枚举权{
读(100)、写(200)、编辑(300);
私有整数值;
私权(整数值){
这个值=值;
}
//从其值获取密钥的反向查找权限
私有静态最终映射查找=新建HashMap();
静止的{
for(右项:Right.values())
lookup.put(item.getValue(),item);
}
公共整数getValue(){
返回值;
}
公共静态右getKey(整数值){
返回lookup.get(值);
}
};
@身份证
@GeneratedValue(策略=GenerationType.AUTO)
@列(name=“AUTHORITY\u ID”)
私人长id;
@列(name=“RIGHT\u ID”)
私有整数rightId;
公共权利{
返回Right.getKey(this.rightId);
}
公共无效设置权(右){
this.rightId=right.getValue();
}
}

问题是,我认为JPA从来没有想到我们可能已经有了一个复杂的预先存在的模式

我认为这导致了两个主要缺点,具体到Enum:

  • 使用name()和ordinal()的限制。为什么不像对待@Entity那样,用@Id标记一个getter呢
  • Enum通常在数据库中具有表示形式,允许与各种元数据关联,包括专有名称、描述性名称,可能还有本地化名称等。我们需要Enum的易用性与实体的灵活性相结合
  • 帮助我的事业并投票表决

    这不比使用@Converter解决问题更优雅吗

    // Note: this code won't work!!
    // it is just a sample of how I *would* want it to work!
    @Enumerated
    public enum Language {
      ENGLISH_US("en-US"),
      ENGLISH_BRITISH("en-BR"),
      FRENCH("fr"),
      FRENCH_CANADIAN("fr-CA");
      @ID
      private String code;
      @Column(name="DESCRIPTION")
      private String description;
    
      Language(String code) {
        this.code = code;
      }
    
      public String getCode() {
        return code;
      }
    
      public String getDescription() {
        return description;
      }
    }
    

    这在JPA 2.1中现在是可能的:

    @Column(name = "RIGHT")
    @Enumerated(EnumType.STRING)
    private Right right;
    
    进一步详情:

    您可以使用JPA2.1

    创建一个枚举类,如下所示:

    public enum NodeType {
    
        ROOT("root-node"),
        BRANCH("branch-node"),
        LEAF("leaf-node");
    
        private final String code;
    
        private NodeType(String code) {
            this.code = code;
        }
    
        public String getCode() {
            return code;
        }
    }
    
    并创建一个如下所示的转换器:

    import javax.persistence.AttributeConverter;
    import javax.persistence.Converter;
    
    @Converter(autoApply = true)
    public class NodeTypeConverter implements AttributeConverter<NodeType, String> {
    
        @Override
        public String convertToDatabaseColumn(NodeType nodeType) {
            return nodeType.getCode();
        }
    
        @Override
        public NodeType convertToEntityAttribute(String dbData) {
            for (NodeType nodeType : NodeType.values()) {
                if (nodeType.getCode().equals(dbData)) {
                    return nodeType;
                }
            }
    
            throw new IllegalArgumentException("Unknown database value:" + dbData);
        }
    }
    

    您使用的
    @Converter(autoApply=true)
    可能因容器而异,但经过测试可以在Wildfly 8.1.0上工作。如果它不起作用,您可以在实体类列中添加
    @Convert(converter=NodeTypeConverter.class)

    我将执行以下操作:

    在自己的文件中单独声明枚举:

    public enum RightEnum {
          READ(100), WRITE(200), EDITOR (300);
    
          private int value;
    
          private RightEnum (int value) { this.value = value; }
    
    
          @Override
          public static Etapa valueOf(Integer value){
               for( RightEnum r : RightEnum .values() ){
                  if ( r.getValue().equals(value))
                     return r;
               }
               return null;//or throw exception
         }
    
          public int getValue() { return value; }
    
    
    }
    
    声明名为Right的新JPA实体

    @Entity
    public class Right{
        @Id
        private Integer id;
        //FIElDS
    
        // constructor
        public Right(RightEnum rightEnum){
              this.id = rightEnum.getValue();
        }
    
        public Right getInstance(RightEnum rightEnum){
              return new Right(rightEnum);
        }
    
    
    }
    
    您还需要一个转换器来接收这些值(仅限JPA 2.1,这里我不讨论使用转换器直接持久化这些枚举的问题,因此它将是单向的)

    通过这种方式,您不能直接设置为enum字段。但是,您可以使用设置权限中的正确字段

    autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
    
    如果需要比较,可以使用:

    authority.getRight().equals( RightEnum.READ ); //for example
    
    我觉得这很酷。它不是完全正确的,因为转换器不打算与enum一起使用。实际上,文档中说,如果不想使用它,应该使用@Enumerated注释。问题是只有两种枚举类型:序数或字符串,但序数比较复杂且不安全


    然而,如果它不能让你满意,你可以做一些更简单的事情(或者不做)

    让我们看看

    右枚举:

    public enum RightEnum {
          READ(100), WRITE(200), EDITOR (300);
    
          private int value;
    
          private RightEnum (int value) { 
                try {
                      this.value= value;
                      final Field field = this.getClass().getSuperclass().getDeclaredField("ordinal");
                      field.setAccessible(true);
                      field.set(this, value);
                 } catch (Exception e) {//or use more multicatch if you use JDK 1.7+
                      throw new RuntimeException(e);
                }
          }
    
    
          @Override
          public static Etapa valueOf(Integer value){
               for( RightEnum r : RightEnum .values() ){
                  if ( r.getValue().equals(value))
                     return r;
               }
               return null;//or throw exception
         }
    
          public int getValue() { return value; }
    
    
    }
    
    以及管理局实体

    @Entity
    @Table(name = "AUTHORITY_")
    public class Authority implements Serializable {
    
    
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Column(name = "AUTHORITY_ID")
      private Long id;
    
    
      // the **Enum** to map (to be persisted or updated) : 
      @Column(name="COLUMN1")
      @Enumerated(EnumType.ORDINAL)
      private RightEnum rightEnum;
    
    }
    
    在第二个想法中,这不是一个完美的情况,因为我们破解了序数属性,但它是一个小得多的编码


    我认为JPA规范应该包括EnumType.ID,其中enum value字段应该使用某种@EnumId注释进行注释。

    最好的方法是将一个唯一的ID映射到每个enum类型,从而避免序号和字符串的陷阱。请参见此部分,其中概述了映射枚举的5种方法

    摘自上面的链接:

    1和2。使用@Enumerated

    目前有两种方法可以使用@E在JPA实体中映射枚举
    @Entity
    @Table(name = "AUTHORITY_")
    public class Authority implements Serializable {
    
    
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Column(name = "AUTHORITY_ID")
      private Long id;
    
      // the **Entity** to map : 
      private Right right;
    
      // the **Enum** to map (not to be persisted or updated) : 
      @Column(name="COLUMN1", insertable = false, updatable = false)
      @Convert(converter = RightEnumConverter.class)
      private RightEnum rightEnum;
    
    }
    
    autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
    
    authority.getRight().equals( RightEnum.READ ); //for example
    
    public enum RightEnum {
          READ(100), WRITE(200), EDITOR (300);
    
          private int value;
    
          private RightEnum (int value) { 
                try {
                      this.value= value;
                      final Field field = this.getClass().getSuperclass().getDeclaredField("ordinal");
                      field.setAccessible(true);
                      field.set(this, value);
                 } catch (Exception e) {//or use more multicatch if you use JDK 1.7+
                      throw new RuntimeException(e);
                }
          }
    
    
          @Override
          public static Etapa valueOf(Integer value){
               for( RightEnum r : RightEnum .values() ){
                  if ( r.getValue().equals(value))
                     return r;
               }
               return null;//or throw exception
         }
    
          public int getValue() { return value; }
    
    
    }
    
    @Entity
    @Table(name = "AUTHORITY_")
    public class Authority implements Serializable {
    
    
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      @Column(name = "AUTHORITY_ID")
      private Long id;
    
    
      // the **Enum** to map (to be persisted or updated) : 
      @Column(name="COLUMN1")
      @Enumerated(EnumType.ORDINAL)
      private RightEnum rightEnum;
    
    }