글 작성자: 자바니또

JPA를 사용하면서 엔티티의 상태 값 중 enum이 있을 때 enum의 순서(상수 값)나 이름(문자열 값)이 아니라 원하는 데이터를 저장하고 꺼내고 싶을 수 있습니다. JPA 2.1부터는 AttributeConverter를 제공하여 이것이 가능하도록 해줍니다.

(JPA 구현체 중 Hibernate 5 기준입니다.)

문제 상황 예시

은행 코드 enum인 BankCode가 있습니다. DB에 저장할 때 BankCode의 코드 값이 DB에 저장되고, 엔티티로 직렬화 할 때도 코드 값을 통해 가능하도록 하고 싶습니다.

엔티티는 다음과 같습니다.

@Entity
public class BankTransaction {

    @Id
    @Column(name = "bank_transaction_id")
    private Long id;


    @Column(name="bank_code", nullable = false)
    private BankCode bankCode;
}

@Getter
@RequiredArgsConstructor
public enum BankCode {

    KB("004", "국민은행"),
    NH("011", "농협은행");

    private final String code;
    private final String bankName;

    public static BankCode ofCode(String code) { /*변환*/ }
}

@Enumerated(value = EnumType.ORDINAL)@Enumerated(value = EnumType.STRING)는 원하는 결과를 만들 수 없습니다. 원하는 건 DB에 004011처럼 코드로 저장 되는 것입니다.

Converter 만들기

  1. javax.persistence.AttributeConverter를 구현한 컨버터 클래스를 만들고 convertToDatabaseColumnconvertToEntityAttribute를 오버라이딩 합니다.
@Converter
public class BankCodePersistConverter implements AttributeConverter<BankCode, String> {

    @Override
    public String convertToDatabaseColumn(BankCode attribute) {
        if (attribute == null) {
            throw new IllegalArgumentException("BankCode가 null입니다.");
        }

        return attribute.getCode();
    }

    @Override
    public BankCode convertToEntityAttribute(String dbData) {

        if (dbData == null || dbData.isBlank()) {
            throw new IllegalArgumentException("dbData가 비어있습니다.");
        }

        return BankCode.ofCode(dbData);
    }
}

두 메서드는 이름에서 알 수 있듯이 DB에 저장할 때와 Entity의 상태 값으로 변환할 때 사용됩니다.

  1. 엔티티의 enum 상태 값에 @Convert를 사용하여 컨버터를 지정합니다.
@Entity
public class BankTransaction {

    @Id
    @Column(name = "bank_transaction_id")
    private Long id;

    @Convert(converter = BankCodePersistConverter.class)    // 컨버터 지정
    @Column(name="bank_code", nullable = false)
    private BankCode bankCode;
}

참고