복붙노트

[SPRING] 고정 값으로 JPA의 enum을 매핑 하시겠습니까?

SPRING

고정 값으로 JPA의 enum을 매핑 하시겠습니까?

JPA를 사용하여 열거 형을 매핑하는 여러 가지 방법을 찾고 있습니다. 나는 특히 각 enum 항목의 정수 값을 설정하고 정수 값만 저장하려고합니다.

@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)를 매핑하고 원하는 값 (100,200,300)을 매핑하지 않습니다.

내가 찾은 두 가지 해결책은 단순하지 않은 것 같습니다 ...

여기에 제안 된 솔루션은 @PrePersist 및 @PostLoad를 사용하여 열거 형을 다른 필드로 변환하고 열거 형 필드를 일시적으로 표시합니다.

@Basic
private int intValueForAnEnum;

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

@PostLoad
void populateTransientFields() {
  right = Right.valueOf(intValueForAnEnum);
}

여기에 제안 된 두 번째 솔루션은 일반적인 변환 객체를 제안했지만 여전히 무겁고 최대 절전 모드로 보입니다 (@Type은 Java EE에는 존재하지 않습니다) :

@Type(
    type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
    parameters = {
            @Parameter(
                name  = "enumClass",                      
                value = "Authority$Right"),
            @Parameter(
                name  = "identifierMethod",
                value = "toInt"),
            @Parameter(
                name  = "valueOfMethod",
                value = "fromInt")
            }
)

몇 가지 아이디어를 염두에두고 있지만 JPA에 존재하는지 여부는 알 수 없습니다.

해결법

  1. ==============================

    1.JPA 2.1보다 이전 버전의 경우 JPA는 열거 형, 이름 또는 서수를 처리하는 두 가지 방법 만 제공합니다. 그리고 표준 JPA는 사용자 정의 유형을 지원하지 않습니다. 그래서:

    JPA 2.1보다 이전 버전의 경우 JPA는 열거 형, 이름 또는 서수를 처리하는 두 가지 방법 만 제공합니다. 그리고 표준 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; }
    
            public static Right parse(int id) {
                Right right = null; // Default
                for (Right item : Right.values()) {
                    if (item.getValue()==id) {
                        right = item;
                        break;
                    }
                }
                return right;
            }
    
        };
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "AUTHORITY_ID")
        private Long id;
    
        @Column(name = "RIGHT_ID")
        private int rightId;
    
        public Right getRight () {
            return Right.parse(this.rightId);
        }
    
        public void setRight(Right right) {
            this.rightId = right.getValue();
        }
    
    }
    
  2. ==============================

    2.이것은 JPA 2.1에서 이제 가능합니다 :

    이것은 JPA 2.1에서 이제 가능합니다 :

    @Column(name = "RIGHT")
    @Enumerated(EnumType.STRING)
    private Right right;
    

    추가 세부 정보 :

  3. ==============================

    3.가장 좋은 방법은 각 열거 형에 고유 한 ID를 매핑하여 ORDINAL 및 STRING의 함정을 피하는 것입니다. 열거 형을 매핑 할 수있는 5 가지 방법을 설명하는이 게시물을 참조하십시오.

    가장 좋은 방법은 각 열거 형에 고유 한 ID를 매핑하여 ORDINAL 및 STRING의 함정을 피하는 것입니다. 열거 형을 매핑 할 수있는 5 가지 방법을 설명하는이 게시물을 참조하십시오.

    위의 링크에서 가져온 것 :

    1 & 2. @Enumerated 사용하기

    현재 @Enumerated 주석을 사용하여 JPA 엔티티 내에서 enum을 매핑 할 수있는 두 가지 방법이 있습니다. 불행히도 EnumType.STRING과 EnumType.ORDINAL에는 모두 한계가 있습니다.

    EnumType.String을 사용하면 열거 형 중 하나의 이름을 바꾸면 열거 형 값이 데이터베이스에 저장된 값과 일치하지 않게됩니다. EnumType.ORDINAL을 사용하면 enum 내에서 유형을 삭제하거나 순서를 변경하면 데이터베이스에 저장된 값이 잘못된 enum 유형으로 매핑됩니다.

    이 두 옵션 모두 허약합니다. 데이터베이스 이주를 수행하지 않고 열거 형을 수정하면 데이터 + 결성을 위] 할 수 있습니다.

    3. 라이프 사이클 콜백

    JPA 수명주기 콜백 주석 인 @PrePersist 및 @PostLoad를 사용하는 것이 해결책입니다. 이는 엔티티에 두 개의 변수가 생기므로 상당히 추한 느낌입니다. 하나는 데이터베이스에 저장된 값을 매핑하고 다른 하나는 실제 열거 형을 매핑합니다.

    4. 각 열거 형에 고유 ID 매핑

    권장되는 솔루션은 열거 형 내에 정의 된 고정 값 또는 ID로 열거 형을 매핑하는 것입니다. 사전 정의 된 고정 값에 매핑하면 코드가 더욱 강력 해집니다. enums 형식의 순서를 수정하거나 이름을 리팩토링해도 부작용이 발생하지 않습니다.

    5. Java EE7 사용하기 @Convert

    JPA 2.1을 사용하는 경우 새 @Convert 주석을 사용할 수 있습니다. 이를 위해서는 각 열거 형에 대해 데이터베이스에 저장되는 값을 정의 할 수있는 @Converter 주석이 붙은 변환기 클래스를 만들어야합니다. 엔티티 내에서 열거 형에 @Convert 주석을 추가합니다.

    내 취향 : (4 번)

    변환기 사용에 반대하는 enum 내에서 내 ID를 정의하는 것을 선호하는 이유는 좋은 캡슐화입니다. enum 타입 만이 그것의 ID를 알고 있어야하고 엔티티 만 enum을 데이터베이스로 매핑하는 방법을 알아야한다.

    코드 예제의 원래 게시물을 참조하십시오.

  4. ==============================

    4.문제는 JPA가 이미 기존의 복잡한 스키마를 가질 수 있다는 생각을 결코 받아들이지 않는다는 것입니다.

    문제는 JPA가 이미 기존의 복잡한 스키마를 가질 수 있다는 생각을 결코 받아들이지 않는다는 것입니다.

    Enum에만 국한된이 두 가지 주요 결점이 있다고 생각합니다.

    내 원인을 도와 JPA_SPEC-47에 투표하십시오.

    이 문제를 해결하기 위해 @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;
      }
    }
    
  5. ==============================

    5.JPA 2.1에서 AttributeConverter를 사용할 수 있습니다.

    JPA 2.1에서 AttributeConverter를 사용할 수 있습니다.

    다음과 같이 열거 형 클래스를 작성하십시오.

    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 : values()) {
                if (nodeType.getCode().equals(dbData)) {
                    return nodeType;
                }
            }
    
            throw new IllegalArgumentException("Unknown database value:" + dbData);
        }
    }
    

    필요한 엔티티 :

    @Column(name = "node_type_code")
    

    @Converter (autoApply = true)는 컨테이너마다 다를 수 있지만 Wildfly 8.1.0에서 작동하도록 테스트되었습니다. 작동하지 않으면 엔티티 클래스 열에 @Convert (converter = NodeTypeConverter.class)를 추가 할 수 있습니다.

  6. ==============================

    6.파스칼의 관련 코드를 닫을 가능성이 있음

    파스칼의 관련 코드를 닫을 가능성이 있음

    @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();
        }
    
    }
    
  7. ==============================

    7.나는 다음을 할 것이다 :

    나는 다음을 할 것이다 :

    자체 파일에 열거 형을 별도로 선언하십시오.

    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 만 해당하며 여기서는 변환기를 사용하여 직접 지속되도록이 열거 형에 대해 논의하지 않을 문제가 있으므로 일방 통행 전용 일 것입니다)

    import mypackage.RightEnum;
    import javax.persistence.AttributeConverter;
    import javax.persistence.Converter;
    
    /**
     * 
     * 
     */
    @Converter(autoApply = true)
    public class RightEnumConverter implements AttributeConverter<RightEnum, Integer>{
    
        @Override //this method shoudn´t be used, but I implemented anyway, just in case
        public Integer convertToDatabaseColumn(RightEnum attribute) {
            return attribute.getValue();
        }
    
        @Override
        public RightEnum convertToEntityAttribute(Integer dbData) {
            return RightEnum.valueOf(dbData);
        }
    
    }
    

    권한 기관 :

    @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
    

    어느 것이 꽤 멋지다 고 생각합니다. 변환기가 enum과 함께 사용되지 않기 때문에 완전히 정확하지는 않습니다. 실제로,이 목적으로 사용하지 말라는 문서에는 대신 @Enumerated 주석을 사용해야합니다. 문제는 ORDINAL 또는 STRING의 두 가지 열거 유형 만 있지만 ORDINAL은 까다 롭고 안전하지 않다는 것입니다.

    그러나, 그것이 당신을 만족시키지 않는다면, 당신은 좀 더 해커하고 더 단순한 (또는하지 않는) 것을 할 수 있습니다.

    보자.

    RightEnum :

    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 스펙에는 enum 값 필드에 @EnumId 주석과 함께 주석을 달아야하는 EnumType.ID가 포함되어야한다고 생각합니다.

  8. from https://stackoverflow.com/questions/2751733/map-enum-in-jpa-with-fixed-values by cc-by-sa and MIT license