JPA에 Enum을 적용해 처리하는 방법에 대해 정리.
JPA를 사용할때 MALE, FEMALE이나 TRUE, FALSE 형태의 코드로 저장하는 컬럼들을 사용할 때 enum을 사용하면 데이터를 관리하기에 용이하다.
강의 내용을 정리하는 포스팅이다보니 굉장히 단순하고 기초적인 내용만 있고 아직 제대로 프로젝트에서 활용해보기 전이기 때문에 좀 더 상세한 내용은 Reference에서 확인.
일단 공부한 환경은 아래와 같다.
- Intelli J
- SpringBoot 2.6.2
- Lombok
- Gradle 7.3.2
예제는 MALE, FEMALE을 저장하는 컬럼이라는 가정하에 진행한다.
public enum Gender {
MALE,
FEMALE
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Entity
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NonNull
private String name;
@NonNull
private String email;
private Gender gender;
}
public interface UserRepository extends JpaRepository<User, Long>{
}
DB 테이블은 User 테이블이고 컬럼은 MySQL기준 BIGINT id, VARCHAR name, VARCHAR email, INT gender가 존재한다.
h2를 통해 data.sql을 사용한다면 굳이 gender 값을 insert 하도록 할 필요는 없다.
테스트코드는 아래와 같이 작성.
@SpringBootTest
class UserRepositoryTest{
@Autowired
private UserRepository userRepository;
@Test
void enumTest(){
User user = userRepository.findById(1L).orElseThrow(RuntimeException::new);
user.setGender(Gender.MALE);
userRepository.save(user);
userRepository.findAll().forEach(System.out::println);
}
이렇게 테스트를 실행해보면 첫번째 데이터에 gender=MALE로 값이 잘 들어가있는것을 확인할 수 있다.
근데 여기서 발생할 수 있는 장애포인트가 있다.
h2가 아닌 다른 데이터베이스를 사용하고 있다면 데이터를 확인했을 때 gender 에 0이 들어가 있는것을 볼 수 있다.
그럼 이제 여기서 수정하고 다시 테스트해볼 부분.
public enum Gender {
FEMALE,
MALE
}
enum클래스를 이렇게 순서를 바꿔서 다시 테스트를 해본뒤에 데이터를 확인해보면 1로 바뀐것을 볼 수 있다.
만약 h2 In-memory DB를 사용하고 있다면 NativeQuery를 만들어서 확인하면 된다.
public interface UserRepository extends JpaRepository<User, Long>{
@Query(value = "select * from user limit 1;", nativeQuery = true)
Map<String, object> findRowRecord();
}
@SpringBootTest
class UserRepositoryTest{
@Autowired
private UserRepository userRepository;
@Test
void enumTest(){
User user = userRepository.findById(1L).orElseThrow(RuntimeException::new);
user.setGender(Gender.MALE);
userRepository.save(user);
userRepository.findAll().forEach(System.out::println);
System.out.println(userRepository.findRowRecord().get("gender"));
}
이렇게 테스트해보면 h2 In-memory DB에 data.sql을 사용하는 환경에서도 확인이 가능하다.
그럼 다시 테스트 결과로 돌아와서 enum 클래스에 MALE, FEMALE 로 작성했을때는 MALE을 save 해준 데이터에
gender 값이 0이고 순서를 바꿔줬을때는 1이 들어가있는것을 확인할 수 있었다.
이유는 @Enumerated에서 확인할 수 있다.
user Entity의 gender위에 @Enumerated를 붙여주고 안에 들어가서 확인해보면

이렇게 되어있는것을 확인할 수 있다.
EnumType value의 default는 ORDINAL이라는 것인데 enum에서 ORDINAL은 zeroIndex를 의미한다.
즉, 배열에서의 index값처럼 0부터 시작하는 형태라는것.
그렇기 때문에 MALE을 먼저 작성했을때는 0이 들어가고 위치를 바꿨을때는 1이 들어가는것이다.
DB에는 이렇게 숫자형태로 들어가지만 JPA Entity에 의해 가져와 출력될때는 Enum클래스를 통해 MALE 혹은 FEMALE로 출력이 되는것이다.
그럼 이제 여기서 발생할 수 있는 장애포인트는 Enum클래스에 상수를 추가하게 된다면 추가 하는 위치에 따른 기존 데이터에서의 오류가 발생하는것이다.
예를들어 성별을 선택하지 않은 경우 NONE이라는 값을 준다고 했을때
public enum Gender {
NONE,
MALE,
FEMALE
}
이렇게 제일 상단에다가 추가해버리면 기존에 MALE로 선택해 저장했기 때문에 0이라는 값을 갖고 있던 데이터들이 모두 NONE으로 출력되는 문제가 발생한다.
그래서 보통 이렇게 default 형태로 저장하는 방법 보다는 상수명 그대로 저장하는 방법을 사용한다고 한다.
물론 포스팅 최 상단에서 언급했듯이 다른 형태로 저장하더라도 직접 명시해 처리하는 방법도 있다.
일단은 그냥 default 상태에서 처리하는 방법만 정리한다.
그럼 이제 이걸 이 상수명 그대로 저장하기 위해서 아래와 같이 수정한다.
@Data
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Entity
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NonNull
private String name;
@NonNull
private String email;
@Enumerated(value = EnumType.STRING)
private Gender gender;
}
그리고 h2 In-memory가 아닌 다른 DB를 사용하는 경우에는 gender 컬럼의 타입을 VARCHAR로 변경해준다.
그리고 원래 들어가있던 숫자형태의 gender 값을 FEMALE로 변경해준다.
gender값을 그대로 유지하거나 삭제만 한 상태로 테스트를 실행하게 되면 IllegalArgumentException이 발생하기 때문.
그리고 다시 테스트를 실행해보면 MALE로 변경된것을 볼 수 있다.
그럼 결과를 보면 @Enumrated(value = EnumType.STRING)을 붙여줌으로써
enum 클래스에 작성한 상수명 대로 값이 들어간다는것을 확인할 수 있다.
강의에서는 이렇기때문에 반드시 String으로 하는것을 추천한다고 하셨지만
아예 추가되거나 삭제될 일이 없는 값들이라면 사용해도 되지 않을까 싶긴 하다.
물론 어떻게 처리되는지에 대한 주석은 필수로 달아야 한다는 조건이 붙을것 같다.
포스팅 전 Jpa Enum으로 검색해 여러 포스팅들을 봤는데 보통은 상수명이 아닌 다른 값으로 넣게 된다면
여기처럼 0, 1 같은 숫자형태가 아닌 상수마다 명시해 처리할 수 있는 방법이 있었다.
이건 아직 해보지도 못했기 때문에 나중에 추가하거나 따로 포스팅..
Reference
- 패스트캠퍼스 java/spring 초격차 패키지 Spring Data JPA
- Jpa Enum 적용
JPA Enum Type 적용기~
Enum를 JPA에 적용하며 있었던 문제와 해결한 내용입니다.
velog.io
'Spring' 카테고리의 다른 글
Jpa Entity 기본 Annotation 2 (@Column, @Transient) (0) | 2022.03.15 |
---|---|
Jpa Entity 기본 Annotation 1 (@GeneratedValue, @Table) (0) | 2022.03.14 |
JPA QueryMethod 2 (where 절 조건 추가) (0) | 2022.02.17 |
JPA QueryMethod 1 (기본 키워드) (0) | 2022.02.14 |
Jpa QueryByExampleExecutor 인터페이스 (0) | 2022.02.08 |