Bepoz
파즈의 공부 일기
Bepoz
전체 방문자
오늘
어제
  • 분류 전체보기 (232)
    • 공부 기록들 (85)
      • 우테코 (17)
    • Spring (85)
    • Java (43)
    • Infra (17)
    • 책 정리 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Bepoz

파즈의 공부 일기

Java

[Java] AttributeConverter를 이용하여 DB에 Entity의 컬렉션 필드 저장하기

2022. 8. 2. 21:54

AttributeConverter를 이용하여 DB에 Entity의 컬렉션 필드 저장하기

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class TimeTravel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Convert(converter = TravelListToJsonConverter.class)
    private List<Travel> travels;

    public TimeTravel(List<Travel> travels) {
        this.travels = travels;
    }

    @Getter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    static public class Travel {
        private String name;
        private String duration;

        public Travel(String name, String duration) {
            this.name = name;
            this.duration = duration;
        }
    }
}

위와 같은 엔티티가 존재하고 DB에 저장을 하려고 한다.
Travel 클래스와 연관관계가 있는 것이 아니고 저 상태로 DB에 저장하고자 할 때, 일반적으로 값 타입 컬렉션 저장을 위해 사용하는 @ElementCollection 을 사용하곤한다. 위와 같이 클래스 타입이 아니라 List<String> 을 저장할 때에도 말이다. 이 방법은 테이블을 따로 두어서 진행하는 방식이다.

오늘 정리할 TIL은 조금 다른 방식이다. 위의 코드의 경우에는 Travel을 JSON 으로 바꿔 String 타입으로 DB에 저장하려고 한다.

public class TravelListToJsonConverter implements AttributeConverter<List<Travel>, String> {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(List<Travel> attribute) {
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<Travel> convertToEntityAttribute(String dbData) {
        TypeReference<List<Travel>> typeReference = new TypeReference<List<Travel>>() {};
        try {
            return objectMapper.readValue(dbData, typeReference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

AttributeConverter 를 구현하는 클래스를 만들고 해당 엔티티 필드에 @Convert(converter = ... .class) 를 이용하여 적용해주면 된다.
중요한 점은 TypeReference 를 이용해야 DB에서 엔티티의 필드로의 Deserializing이 원활하게 이루어진다는 점이다.
convertToDatabaseColumn 메서드는 이름 그대로 엔티티를 DB에 집어넣을 때 어떤 식으로 넣을지에 대한 구현이고,
convertToEntityAttribute 메서드는 DB에서 빼와서 엔티티 필드로 변환할 때의 구현 내용이다.

{
    "travels": [
        {
            "name": "seoul",
            "duration": "maybe 2weeks?"
        },
        {
            "name": "new york",
            "duration": "a month"
        }
    ]
}

위의 값을 Body로 하여 TimeTravel 생성 요청을 해보았다.

image

그 결과 DB에 위와 같이 JSON 형식으로 잘 저장된 것을 확인할 수가 있다.

image

조회 시에도 잘 조합되어 조회되는 것을 확인할 수가 있다.


클래스를 가지는 컬렉션이 아니라 String 에 대한 것도 한 번 확인해보겠다.

public class TimeTravel {
...

    // 새로 추가된 필드
    @Convert(converter = DelimiterConverter.class)
    private List<String> countries;
...
}

-------------------------------------------------------------------------------------------------------------

public class DelimiterConverter implements AttributeConverter<List<String>, String> {

    private final String DELIMITER = ",";

    @Override
    public String convertToDatabaseColumn(List<String> attribute) {
        return String.join(DELIMITER, attribute);
    }

    @Override
    public List<String> convertToEntityAttribute(String dbData) {
        return new ArrayList<>(Arrays.asList(dbData.split(",")));
    }
}

List<String> 타입을 가지는 필드를 추가하고 간략하게 converter 작성을 하여 결과를 확인해보겠다.

image

, 가 붙어 잘 들어갔다.

image

조회 시에도 문제 없이 조회된다.

아무래도 클래스를 가지는 List 보다는 String 을 가지는 List의 경우가 훨씬 유용할 것이라고 생각된다.
컨버터를 이용하여 이렇게 List를 저장할 때 사용하는 것 뿐만 아니라 DB에 넣고 뺄 때에 암호화/복호화가 필요한 경우에도 convertToDatabaseColumn , convertToEntityAttribute 메서드를 구현하면서 이곳에서 암,복호화를 진행하는 방법으로 이용할 수도 있다.


'Java' 카테고리의 다른 글

[Kotlin] 기본 생성자가 없고, 필드가 1개여도 코틀린은 역직렬화가 된다  (0) 2023.05.12
[Java] ThreadLocal에 대해  (0) 2021.12.05
[Java] Atomic, Volatile, Synchronized 에 대해  (2) 2021.09.20
[Java] Reflection 사용법 정리  (0) 2021.09.08
[Java] ExecutorService 와 ThreadPoolExecutor 에 대해  (0) 2021.09.06
    'Java' 카테고리의 다른 글
    • [Kotlin] 기본 생성자가 없고, 필드가 1개여도 코틀린은 역직렬화가 된다
    • [Java] ThreadLocal에 대해
    • [Java] Atomic, Volatile, Synchronized 에 대해
    • [Java] Reflection 사용법 정리
    Bepoz
    Bepoz
    https://github.com/Be-poz/TIL

    티스토리툴바