JPA에서 Java Enum Type 이용하기


Enum Type

Jave의 Enum Type은 상수의 그룹을 나타 낼때 사용된다.

public enum Color {
    RED, GREEN, BLUE;
}

JPA에서 Java Enum Type 이용하기

DataBase에서 특정 몇개의 값을 가지는 Attribute가 존재한다면 JPA의 해당 Entity에 field로 Enum Type을 사용하여 나타낼 수 있다.


아래와 같은 데이터 베이스를 가정하여보자

Table Name User      
Attribute Name id user_name password role
Attribute Description P.K     F.K
Row 1 0 user1 1q2w3e4r 0
Row 2 1 user2 password 0
Row 3 2 admin1 qwerty 1
Table Name Role  
Attribute Name id detail
Attribute Description P.K  
Row 1 0 USER
Row 2 1 ADMIN

이 테이블을 JPA Entity로 나타내면 다음과 같다.

@Entity
@Getter
@Setter
@NoArgsConstructor
public Class User {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(name="user_name")
    private String userName;
    @Column
    private String password;
    @OneToOne
    private Role role;
}
@Entity
@Getter
@Setter
@NoArgsConstructor
public Class Role {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column
    private String detail;
}

이런 식으로 구현을 하면 User 객체를 가져오면서 Role또한 함께 가져올 수 있다.
하지만 매번 User 객체를 가져올 때, Role을 Join해서 가져온다.
또한 필요한 Role 객체는 id=0 detail="USER"인 객체와 id=1 detail="ADMIN"인 객체 단 두개 뿐이지만 매 User마다 하나 씩의 Role 객체를 생성하여 참조하게 된다.
이 문제를 해결하기 위해 Java Enum Type을 이용할 수 있다.


@Entity
@Getter
@Setter
@NoArgsConstructor
public Class User {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(name="user_name")
    private String userName;
    @Column
    private String password;
    @Enumerated(EnumType.ORDINAL)
    private Role role;
}
public enum Role {
    USER, ADMIN;
}

위 와 같이 @Enumerated 어노테이션을 이용하면 EnumType을 사용 할 수 있다.
EnumType.ORDINAL로 설정하면 Enum Type의 순서가 DB에 저장된다. [0, 1]
EnumType.String으로 설정하면 Enum Type의 이름이 DB에 저장된다. [USER, ADMIN]


name도 순서도 아닌 또다른 값을 이용해야 한다면?

같은 Enum Type을 사용하지만 DB에는 다른 값이 들어가는 테이블이 있다면 어떻게 해야할까?

@Getter
public enum Role {
    USER("ROLE_USER"), ADMIN("ROLE_ADMIN");

    private String customType;

    Role (String customType){
        this.customType = customType;
    }
}

조건에 따라 [0, 1]나 [USER, ADMIN]이 아닌 [ROLE_USER, ROLE_ADMIN]을 DB에 저장해야 하는 경우에는 다음과 같이 해결할 수 있다.

@Entity
@Getter
@Setter
@NoArgsConstructor
public Class User {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(name="user_name")
    private String userName;
    @Column
    private String password;
    @Convert(converter = roleConverter.class)
    private Role role;
}
@Converter
public class myConverter implements AttributeConverter<Role, String>{

    @Override
    public String convertToDatabaseColumn(Role attribute) {
        /* 객체에서 DB column으로 변경 */
        if (conditionTest(attribute)){
            return attribute.name();
        }
        return attribute.getCustomType();
    }

    @Override
    public Role convertToEntityAttribute(String dbData) {
        /* DB column에서 객체로 변경 */
        if (conditionTest(dbDate)){
            return Role.valueOf(dbData);
        }
        for (Role role : Role.values()){
            if (role.getCustomType().equals(dbData)){
                return role;
            }
        }

        throw new NoSuchRoleTypeException(dbData);
    }
}

위와 같이 AttributeConverter<변경을 원하는 객체, DB에 저장될 데이터>를 구현하여 해결할 수 있다.
AttributeConverter는 Enum Type뿐만 아니라 객체에도 사용할 수 있다.