티스토리 뷰

[ Enum ] JPA로 enum name 그대로 DB에 저장하기(@Enumerated)

 

[ Enum ] JPA로 enum name 그대로 DB에 저장하기(@Enumerated)

최근에 Enum을 이렇게도 저렇게도 사용해보게 되었다. 그 동안 회사 프로젝트에서는 왜 사용을 안 했는지 정말 후회되는 Enum... 나 처럼 enum을 처음 사용해보는 사람은 JPA를 사용할 때 entity에 enum c

pamyferret.tistory.com

위의 전 글에서 enum의 값을 DB에 name 그대로 저장하기 위해서 @Enumerated 어노테이션을 사용했었다. 하지만 이럴 경우 enum의 값이 변경 되었을 경우나 order 순서가 변경되었을 때 DB에 저장된 값과 enum의 값이 매칭되지 않아 문제가 발생할 수 있다. 이렇게 값이 변경되는 경우에는 변경된 값에 따른 별도의 로직을 넣어줘야하는데, @Enumerated 어노테이션은 말 그대로 어노테이션이어서 따로 무언가 로직을 첨가할 수 없다.

그래서 enum를 다룰 때 @Enumerated 어노테이션 대신 AttributeConverter을 구현한 class에 @Converter 어노테이션을 사용하면 DB에 원하는 enum의 값을 저장할 수 있습니다.

 

 

@Converter이란?

@Converter 어노테이션은 엔티티의 데이터를 DB에서 가져올 때 변환하는 방법에 대한 로직과 DB에서 엔티티의 데이터를 가져올 때의 변환하는 방법에 대한 로직을 정의해놓은 변환하는 클래스에 붙이는 어노테이션이다.

@Converter(autoApply = true)로 제일 많이 쓰이는데 이 뜻은 해당 변환 클래스에 지정된 타입에 대해서는 모두 해당 변환 클래스의 메소드를 이용해 DB와의 통신에서 값을 변환하겠다는 것이고, autoApply 옵션은 기본 값이 false로 따로 XML과 같은 설정 파일에 해당 변환 클래스를 사용할 곳에서만 변환이 이루어지게 된다.

 

 

AttributeConverter 인터페이스 구현

AttributeConverter를 implements한 클래스를 생성하면 아래의 두 함수를 오버라이딩하게 되어 있다.

@Converter(autoApply = true)
public class MenuConverter implements AttributeConverter<Menu, String>{
	@Override
	public String convertToDatabaseColumn(Menu menu) {
		...
	}

	@Override
	public Menu convertToEntityAttribute(String menuName) {
		...
	}
 }

 

AttributeConverter를 구현할 때 AttributeConverter안에 어떤 엔티티를 사용할 것이며 해당 엔티티가 DB에 어떤 타입으로 저장되어 있는지 넣도록 되어 있다. 첫 번째에는 변환에 사용할 엔티티, 여기서는 enum class를 넣으면 되고 여기 예제에서는 메뉴의 이름을 텍스트로 DB에 저장하므로 String이라고 타입을 지정해줬다.

 

 

두 함수의 이름을 딱 보면 이게 어디에 쓰이는 함수겠구나~하고 감이 오겠지만 간단히 설명하면 아래와 같다.

- convertToDatabaseColumn(Menu menu) : enum을 DB에 어떤 값으로 넣을 것인지 정의

- converToEntityAttribute(String menuName) : DB에서 읽힌 값에 따라 어떻게 enum랑 매칭 시킬 것인지 정의

 

 

예를 들면 아래와 같이 두 함수를 오버라이딩 할 수 있다.

@Converter(autoApply = true)
public class MenuConverter implements AttributeConverter<Menu, String>{

	@Override
	public String convertToDatabaseColumn(Menu menu) {
		if(menu == null) {
			 return null;
		}
		return menu.getMenuName();
	}

	@Override
	public Menu convertToEntityAttribute(String menuName) {
		if(menuName == null) {
			return null;
		}
		
		return Stream.of(Menu.values())
				.filter(m -> m.getMenuName().equals(menuName))
				.findFirst()
				.orElse(null);
	}

}

 

우선 convertToDatabaseColumn(Enum => DB)에서는 menu가 null일 경우네는 null을, null이 아닐 경우 enum에서 메뉴이름을 정의해둔 getMenuName() 함수로 return 하도록 정의했다.

@Override
public String convertToDatabaseColumn(Menu menu) {
	if(menu == null) {
			return null;
	}
	return menu.getMenuName();
}

 

 

convertToEntityAttribute(DB => Enum)에서는 마찬가지로 menuName이 null이 아닐 경우네는 Stream.of()함수를 통해 enum에 있는 값들을 모두 가져오고 거기서 filter함수를 사용해서 getMenuName()으로 나오는 메뉴 이름과 일치하는 것을 찾았다. 그리고 찾은 것에서 findFirst()를 해서 찾은 것의 첫 번째를 return하고 orElse()를 통해 많이 일치되는게 없을 경우 null 값을 return 하도록 했다.

@Override
public Menu convertToEntityAttribute(String menuName) {
	if(menuName == null) {
		return null;
	}
		
	return Stream.of(Menu.values())
			.filter(m -> m.getMenuName().equals(menuName))
			.findFirst()
			.orElse(null);
}

 

 

위 코드에서는 별도로 enum의 값이 변환 된 것에 대한 커스텀 로직을 넣지 않았지만 enum의 값이 변환되어 미치못하게 예외 로직을 짜서 넣어야할 경우 위의 convertToDatabaseColumn(), convertToEntityAttribute() 함수에 해당 로직을 적절하게 짜 넣으면 enum과 DB 사이에서 값이 적절하게 변환되어 에러 없이 사용할 수 있게 된다.

 

 

AttributeConverter로 구현 Converter를 구현하면 Enum 클래스의 @JsonCreator를 대신할 수 있는가?

일단 결론부터 말하면 아니다다.

@JsonCreator는 entity를 Controller에서 RequestBody와 같이 JSON 형태로 받을 때 동작이 일어난다. 하지만 AttributeConverter는 entity와 DB 사이의 변환 역할을 해주는 함수를 구현하는 인터페이스이다.

따라서 request에서 entity의 값이 parse 되는데에는 Converter가 아닌 @JsonCreator와 같은 방법을 사용해야 한다.

 

 

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함