[Spring] DB에 배열을 문자열로 저장하기

김재연·2023년 6월 3일
1

수숙관

목록 보기
14/17
post-thumbnail

🤔 문제상황

요청은 배열로 여러개의 데이터를 받는데 DB에 배열 요소를 하나씩 저장하기는 비효율적이라 배열을 통째로 컬럼 한개에 저장하고 싶은 상황

  • Request : [A,B,C] => 3개의 데이터를 보냄
  • DataBase :
    1. A, B, C => 3개를 각각 저장하는 건 X
    2. [A, B, C] => 통째로 묶어서 1개로 저장 O

💡 해결방법

Entity와 DB사이에서 속성의 변환을 담당하는 AttributeConverter를 활용해서 배열을 통째로 문자열로 변환해 저장한다.

  • 조회 : DB의 데이터 읽기 → Converter(convertToEntityAttribute)의 특정 동작 수행 → 그 결과를 Entity 속성값에 입력
  • 저장/수정 : Entity 속성값 읽기 → Converter(convertToDatabaseColumn)의 특정 동작 수행 → 그 결과를 DB의 컬럼에 저장

- 코드

/* Converter */
public class MyConverter implements AttributeConverter<List<DATATYPE>, String> {
    /* DB에 저장될 때 : List<DATATYPE> -> String */
    @Override
    public String convertToDatabaseColumn(List<DATATYPE> attribute) {
        ...
    }

    /* DB에서 조회할 때 : String -> List<DATATYPE> */
    @Override
    public List<DATATYPE> convertToEntityAttribute(String dbData) {
        ...
    }
}
/* Entity */
public class Assignment {
    ...
    /* 해당 필드 저장/조회에 사용될 Converter 지정 */
    @Convert(converter = MyConverter.class)
    private List<DATATYPE> arrayData;
}

1. LongListConverter

Long형 배열을 문자열로 변환해서 저장 & 문자열을 Long형 배열로 변환해서 조회하는 Converter

- 코드

/* LongListConverter.java */
import com.fasterxml.jackson.core.type.TypeReference;

public class LongListConverter implements AttributeConverter<List<Long>, String> {
    private static final ObjectMapper mapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);

    @Override
    public String convertToDatabaseColumn(List<Long> attribute) {
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException();
        }
    }

    @Override
    public List<Long> convertToEntityAttribute(String dbData) {
    	TypeReference<List<Long>> typeReference = new TypeReference<List<Long>>() {};
        try {
            return mapper.readValue(dbData, typeReference);
        } catch (IOException e) {
            throw new IllegalArgumentException();
        }
    }
}
/* Assignment.java */
public class Assignment {
    ...
    @Column(nullable = false)
    @Convert(converter = LongListConverter.class)
    private List<Long> frequency;
}

➕ 2023.06.17 추가
엔티티->디비 저장은 잘됐는데, 디비->엔티티는 딱히 테스트할 일이 없어서 잘못된 코드를 써놓고도 몰랐다. 이번에 디비에서 해당값을 읽어와서 사용하는데 자꾸 캐스팅 오류가 나서 (Integer는 Long으로 캐스팅할 수 없다는 오류) 코드를 제대로 바꿔줬다.

// 이전
mapper.readValue(dbData, List.class);
// 현재
TypeReference<List<Long>> typeReference = new TypeReference<List<Long>>() {};
mapper.readValue(dbData, typeReference);

- 결과

  • 요청
{
	...
    "frequency" : [2,5]
}
  • DB

2. StringListConverter

String형 배열도 똑같이 쓰면 된다.

- 코드

/* StringListConverter.java */
public class StringListConverter implements AttributeConverter<List<String>, String> {
    private static final ObjectMapper mapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);

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

    @Override
    public List<String> convertToEntityAttribute(String dbData) {
    	TypeReference<List<String>> typeReference = new TypeReference<List<String>>() {};
        try {
            return mapper.readValue(dbData, typeReference);
        } catch (IOException e) {
            throw new IllegalArgumentException();
        }
    }
}
/* Submit.java */
public class Submit {
    ...
    @Column(nullable = false, length = 1000)
    @Convert(converter = StringListConverter.class)
    private List<String> imageUrl;
}

문자열의 경우, 전체 길이가 디폴트값(varchar(255))으로 부족해서 length 속성을 활용해 최대 길이를 varchar(1000)으로 늘려줬다.

💣 ddl-auto = update로 해놔서 그런지 변경사항이 안먹혀서 테이블을 아예 날리고 새로 만들었다.

- 결과

  • 요청

  • DB


Reference

[Spring] 테이블의 컬럼 하나에 N개의 값을 넣고 싶다면?
[Java] AttributeConverter를 이용하여 DB에 Entity의 컬렉션 필드 저장하기

profile
일기장같은 공부기록📝

0개의 댓글