본문 바로가기
JPA

JPA column에 list를 넣는 방법(String 변환, @ElementCollection)

by khds 2021. 12. 11.

 

들어가기

 

Springboot와 JPA를 통해 프로젝트를 진행하던 중 간단한 값들의 리스트를 하나의 엔티티에 넣어두고 싶을 때가 있었다. 

이는 '@OneToMany'를 통해서 새로운 테이블과 연관관계를 맺으면 쉽게 구현할 수 있지만...

새로운 테이블을 만들고 조인을 하는 등 복잡한 방법을 쓰고 싶지가 않았다. 오직 원하는 테이블만을 위한 리스트 필드를 가지고 싶은 것이다. 

이럴 때 어떻게 사용할 수 있을까?

이 글에서는 간단하게 1. String으로의 변환 2. @ElementCollection 두가지 방법으로 소개하려고 한다.

 

 

본론 

 

1. String 타입으로 변환

 

첫번째로 리스트를 Stirng 타입으로 변환하여 저장하는 것이다.

아래의 사진을 봐보자.

 

위는 특정 요청을 했을 때 응답 값을 JSON 형식으로 응답한 것이다. 

Springboot를 사용하다 보면 JSON 형식의 데이터가 이동되는 것을 쉽게 볼 수 있었을 것이다. 

 

여기서는 이러한 JSON 형식의 String 값으로서 테이블에 리스트를 저장할 것이다.  

 

아래는 엔티티의 구조이다.

 

@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class TestEntity {

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

    private String title;

    @Convert(converter = StringListConverter.class)
    private List<String> columns;
}

 

 

주요하게 볼 것은 columns이다. 엔티티를 기반으로 데이터베이스에 테이블이 매칭이 되기 때문에 필드에 List 값을 넣는다면 에러가 발생한다. 하지만 위 코드에서는 에러가 발생하지 않는다. 그 이유는 @Convert 덕분이다. 이는 리스트를 자동으로 StringListConverter.class에 정의한 값으로 변환을 해주기 때문에 데이터베이스 상에서는 지정한 타입이 값이 테이블에 들어간다.

 

아래는 간단하게 구현한 StringListConverter.class 이다.

 

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

    private final ObjectMapper mapper = new ObjectMapper();

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

    @Override
    public List<String> convertToEntityAttribute(String data) {
        try {
            return mapper.readValue(data, List.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

 

 

중요한 부분은 'converToDatabaseColumn'과 'converToEntityAttribute'이다. 이 두 가지 덕분에 @Convert를 단 칼럼에 자동으로 리스트와 String의 변환이 이루어지는 것이다.

내용은 단순하게 ObjectMapper를 통해 리스트를 JSON 형식으로, JSON형식을 리스트로 변환한다. 

 

그렇다면 데이터는 어떻게 저장이 되고, 어떻게 조회가 될까?

 

리스트가 columns라는 필드에 String 타입의 JSON 형식으로 저장이 된 것을 알 수 있다.

 

 

 

조회할 때는 다시 리스트 형식으로 조회가 되는 것을 알 수 있다.

 

 

 

JSON 타입 말고 자신만의 형식으로 저장을 하고 싶을 수 있다.

그럴 때는 StringListConverter를 자신만의 방식으로 설정하면 된다. 아래는 JSON 형식이 아닌 다른 형식으로 리스트를 저장한 예시이다. 

 

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
    private static final String SPLIT_CHAR = ", ";

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

    @Override
    public List<String> convertToEntityAttribute(String string) {
        return Arrays.asList(string.split(SPLIT_CHAR));
    }
}

 

 

'

 

 

 

 

2. '@ElementCollection'을 통한 컬렉션 테이블 생성

 

@ElementCollection 어노테이션을 사용하는 방식으로 위 어노테이션을 추가한 칼럼은 주키가 원래 테이블의 주키이고 컬렉션 값들을 가지는 테이블이 새로 생성된다. 

아래의 사진을 봐보자.

 

 

 

 

 

흡사 @OneToMany를 사용한 것 같다. 하지만 이는 @OneToMany와 다르다. 오직 부모 테이블과 연결해서만 이 테이블에 접근할 수 있고, 부모 테이블에서 위 리스트를 접근할 때 join이 발생하지 않는다. 생성될 때나 삭제될 때나 모두 같이 삭제되는 즉, 하나의 테이블 연결채로 볼 수가 있다. 이는 부모 테이블에 종속되어 있다고 할 수 있다.

 

아래는 구현된 코드이다.

 

@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class TestEntity {

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

    private String title;

    @ElementCollection(fetch = FetchType.LAZY)
    private List<String> columns;
}

 

 

단순히 위 어노테이션을 추가해주기만 하면 된다. 

 

 

 

 

참고

 

http://daplus.net/java-jpa%EC%97%90%EC%84%9C-list-string-%EC%9C%A0%ED%98%95%EC%9D%98-%EC%86%8D%EC%84%B1%EC%9D%84-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85/

 

[java] JPA에서 List <String> 유형의 속성을 유지하는 방법은 무엇입니까? - 리뷰나라

List 유형의 필드를 가진 엔티티를 유지하는 가장 똑똑한 방법은 무엇입니까? Command.java package persistlistofstring; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.Basic;

daplus.net

 

https://prohannah.tistory.com/133

 

JPA @ElementCollection

RDB에는 컬렉션과 같은 형태의 데이터를 컬럼에 저장할 수 없기 때문에, 별도의 테이블을 생성하여 컬렉션을 관리해야한다. 이때 컬렉션 객체임을 JPA에게 알려주는 어노테이션이 @ElementCollection

prohannah.tistory.com

 

https://passionfruit200.tistory.com/346

 

[SpringBoot, JPA] 회원과 회원권한관리에서 OneToMany 대신 ElementCollection을 사용하는 경우

왜 이 글을 작성하였을까 Spring-data-jpa를 활용하여, 회원(Member Entity)의 회원권한(MemberRole)에 관한 테이블 설계를 진행하고 있었습니다. 이때, 회원과 회원권한을 OneToMany를 사용하여 설계를 진행하

passionfruit200.tistory.com