고정 값을 가진 JPA의 enum을 매핑 하시겠습니까?
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과 함께 Enumerated 주석을 사용하는 것입니다.
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
그러나이 경우 JPA는 열거 값 (0,1,2)을 매핑하고 원하는 값 (100,200,300)이 아닙니다.
내가 찾은 두 가지 해결책은 간단하지 않습니다 ...
첫 번째 해결책
here 제안 된 솔루션 은 @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에 존재하는지 모르겠습니다.
- Authority 오브젝트를로드 및 저장할 때 Authority 클래스의 올바른 멤버의 setter 및 getter 메소드를 사용하십시오.
- 동등한 아이디어는 JPA에게 enum을 int로, int를 enum으로 변환하는 Right enum의 방법이 무엇인지 알려주는 것입니다.
- Spring을 사용하고 있기 때문에 JPA에게 특정 변환기 (RightEditor)를 사용하도록 지시하는 방법이 있습니까?
JPA 2.1 이전 버전의 경우 JPA는 두 가지 방법으로 열거 형을 처리 name
할 수 ordinal
있습니다. 표준 JPA는 사용자 정의 유형을 지원하지 않습니다. 그래서:
- 사용자 정의 유형 변환을 수행하려면 공급자 확장 (Hibernate
UserType
, EclipseLinkConverter
등) 을 사용해야합니다 . (두 번째 해결책). ~ 또는 ~ - @PrePersist 및 @PostLoad 트릭 (첫 번째 솔루션)을 사용해야합니다. ~ 또는 ~
- getter 및 setter에 주석을 달고
int
값을 가져 오거나 ~ 또는 ~ - 엔티티 레벨에서 정수 속성을 사용하고 getter 및 setter에서 변환을 수행하십시오.
최신 옵션을 설명하겠습니다 (기본 구현이므로 필요에 따라 조정하십시오).
@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();
}
}
이제 JPA 2.1에서 가능합니다.
@Column(name = "RIGHT")
@Enumerated(EnumType.STRING)
private Right right;
자세한 내용은 :
- http://java.dzone.com/articles/mapping-enums-done-right
- http://www.thoughts-on-java.org/jpa-21-how-to-implement-type-converter/
최선의 방법은 고유 한 ID를 각 열거 형 유형에 매핑하여 ORDINAL 및 STRING의 함정을 피하는 것입니다. 열거 형을 매핑 할 수있는 5 가지 방법을 간략하게 설명하는 이 게시물 을 참조하십시오 .
위의 링크에서 가져온 것 :
1 & 2. @Enumerated 사용
@Enumerated 어노테이션을 사용하여 JPA 엔티티 내에 열거를 맵핑 할 수있는 방법은 현재 2 가지가 있습니다. 불행히도 EnumType.STRING 및 EnumType.ORDINAL에는 제한이 있습니다.
EnumType.String을 사용하는 경우 열거 형 유형 중 하나의 이름을 바꾸면 열거 형 값이 데이터베이스에 저장된 값과 동기화되지 않습니다. EnumType.ORDINAL을 사용하는 경우 열거 형 내에서 형식을 삭제하거나 재정렬하면 데이터베이스에 저장된 값이 잘못된 열거 형 형식에 매핑됩니다.
이러한 옵션은 모두 취약합니다. 데이터베이스 마이그레이션을 수행하지 않고 열거 형을 수정하면 데이터 무결성이 저하 될 수 있습니다.
3. 라이프 사이클 콜백
가능한 해결책은 JPA 라이프 사이클 콜백 주석, @PrePersist 및 @PostLoad를 사용하는 것입니다. 이제 엔터티에 두 개의 변수가 있으므로 상당히 추한 느낌입니다. 하나는 데이터베이스에 저장된 값을 매핑하고 다른 하나는 실제 열거 형을 매핑합니다.
4. 고유 한 ID를 각 열거 형에 매핑
선호되는 솔루션은 열거 형에 정의 된 고정 값 또는 ID에 열거 형을 매핑하는 것입니다. 미리 정의 된 고정 값에 매핑하면 코드가 더욱 강력 해집니다. 열거 형 순서 또는 이름의 리팩토링 순서를 수정해도 악영향을 미치지 않습니다.
5. Java EE7 @Convert 사용
JPA 2.1을 사용하는 경우 새로운 @Convert 주석을 사용하는 옵션이 있습니다. 이를 위해서는 @Converter로 주석이 달린 변환기 클래스를 만들어야합니다. 여기에서 각 열거 형 유형에 대해 데이터베이스에 저장할 값을 정의 할 수 있습니다. 엔티티 내에서 @Convert로 열거에 주석을 달 것입니다.
나의 선호도 : (번호 4)
변환기를 사용하는 대신 열거 형 내에서 ID를 정의하는 것을 선호하는 이유는 캡슐화가 좋기 때문입니다. 열거 형 만 ID를 알고 있어야하며 엔티티 만 열거 형을 데이터베이스에 매핑하는 방법을 알아야합니다.
코드 예제 는 원래 게시물 을 참조하십시오 .
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 : 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)
엔터티 클래스 열을 추가 할 수 있습니다 .
문제는 JPA가 이미 존재하는 복잡한 기존 스키마를 가질 수 있다는 생각을 결코 받아들이지 않았다는 것입니다.
Enum과 관련하여 두 가지 주요 단점이 있다고 생각합니다.
- name () 및 ordinal () 사용의 제한 사항. @Entity를 사용하는 방식 인 @Id로 getter를 표시하지 않는 이유는 무엇입니까?
- Enum은 일반적으로 데이터베이스에 적절한 이름, 설명이 포함 된 이름, 현지화 등을 포함한 모든 종류의 메타 데이터와 연결할 수 있도록 표현되어 있습니다. 엔터티의 유연성과 결합 된 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;
}
}
파스칼 관련 코드를 닫을 수 있습니다.
@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();
}
}
나는 다음을 할 것입니다 :
자체 파일에 열거 형을 별도로 선언하십시오.
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;
}
이 방법으로 enum 필드에 직접 설정할 수 없습니다. 그러나 다음을 사용하여 권한에서 권한 필드를 설정할 수 있습니다
autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
비교해야 할 경우 다음을 사용할 수 있습니다.
authority.getRight().equals( RightEnum.READ ); //for example
Which is pretty cool, I think. It´s not totally correct, since the converter it´s not intended to be use with enum´s. Actually, the documentation says to never use it for this purpose, you should use the @Enumerated annotation instead. The problem is that there are only two enum types: ORDINAL or STRING, but the ORDINAL is tricky and not safe.
However, if it doesn´t satisfy you, you can do something a little more hacky and simpler (or not).
Let´s see.
The 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; }
}
and the Authority entity
@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;
}
In this second idea, its not a perfect situation since we hack the ordinal attribute, but it´s a much smaller coding.
I think that the JPA specification should include the EnumType.ID where the enum value field should be annotated with some kind of @EnumId annotation.
참고URL : https://stackoverflow.com/questions/2751733/map-enum-in-jpa-with-fixed-values
'Programming' 카테고리의 다른 글
물결표가 표현식 앞에있을 때 어떤 역할을합니까? (0) | 2020.05.20 |
---|---|
SQL Server 스키마는 무엇입니까? (0) | 2020.05.19 |
Python-초기 용량을 가진 목록 만들기 (0) | 2020.05.19 |
LINQ 삽입 후 'id'필드를 반환 할 수 있습니까? (0) | 2020.05.19 |
파이썬을 이용한 웹 스크랩 핑 (0) | 2020.05.19 |