개발 공부 기록
728x90

목표

  • 기존 Varchar Type 컬럼에 Json 형식으로 저장된 문자열을 Entity의 Map 컬럼에 매핑하자

배경

JSON Type을 사용하던 DB에서, 티베로(오라클)로 변경하게 되어 JSON Type을 쓸 수 없게 되었다. (티베로 7부터 JSON Type을 제공한다)

그래서 해당 DB 컬럼의 Data Type을 Varchar로 변경하고, JSON 형식으로 저장한 내용을 서버 단에서 Map으로 활용하고 싶었다.

 

JPA의 Entity의 Map 은 유지하면서, 기존 MariaDB, MySQL의 JSON Type을 티베로의 Varchar Type으로 전환하는 과정을 살펴보자!

 

예시

'MESSAGE' Table 값

id(Long) info(varchar(2000))
1 {"content":"내용내용내용"}
2 {"content":"안녕하세요?", "date":"2023/02/02"}
3 {"content":"배열을 넣어볼까", "list":[{"name":"강백호", "age":20},{"name":"정대만", "age":20}]}

수정 전 Message 클래스 (MariaDB, MySQL 이용)

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
@Entity
@Table(name = "MESSAGE")
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Column(columnDefinition = "JSON") //ddl-auto:create 하여 JSON 타입 컬럼으로 생성시키기 위함
    @Type(type = "json") //기존 RDB의 JSON type을 Map에 매핑시키기 위함 -> 티베로(오라클)로 바꾸기 전
    private Map<String, Object> info;
}

 

MariaDB, MySQL을 이용하면 이런 방식으로 충분히 info 변수 안에 MESSAGE 테이블의 info 컬럼 값들이 알아서 key, value를 찾아 저장된다.

 

적용

그럼 티베로 혹은 JSON type을 지원하지 않는 RDB의 Varchar Type을 Map에 key-value 매핑을 해보자.

 

이 블로그를 참고해서 진행했다ㅎㅎ 읽어보면 어떤 식으로 진행해야 하는지 알 수 있을 것이다.

https://medium.com/@nooneypradeep/store-json-as-varchar-in-oracle-db-12c-spring-boot-jpa-e5596c951c87

 

Store JSON as VARCHAR in Oracle DB 12c ,Spring Boot JPA

Here I will explain how to store json in oracle db using spring boot Jpa,  Oracle 12c spring boot 2.6

medium.com

일단 String To Map, Map To String 을 변환하기 위해서는 AttributeConverter 가 필요하다. AttributeConverter interface를 구현해보자.

 

위 블로그와 다르게 ObjectMapperConfig 클래스를 이용하여 ObjectMapper 빈 설정을 해주었다.

ObjectMapper는 생성 비용이 비싸다고 하니 싱글톤으로 관리해주자!

 

ObjectMapper Config 클래스

@Configuration
public class ObjectMapperConfig {

  @Bean
  public ObjectMapper objectMapper() {
    return new ObjectMapper()
        .registerModule(new JavaTimeModule())
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }
}

 

AttributeConverter 구현체

@Converter
public class MapAndStringConverter implements AttributeConverter<Map<String, Object>, String> {

    private final ObjectMapper objectMapper;
    
    public MapAndStringConverter(ObjectMapper objectMapper) {
    	this.objectMapper = objectMapper;
    } 

    @Override
    public String convertToDatabaseColumn(Map<String, Object> attribute){
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> convertToEntityAttribute(String attribute) {
        if(attribute == null) return new HashMap<>();

        try {
            return objectMapper.readValue(attribute, Map.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new HashMap<>();
    }

참고한 블로그에서는 JsonMap 이라는 클래스를 작성하여, Entity의 변수 타입을 JsonMap으로 진행하였지만, 나는 Map으로 바로 받고 싶었기 때문에 이렇게 진행했다.

 

ObjectMapper는 생성자를 이용해 빈 주입을 받았다.

 

수정 후 Message 클래스 (JSON 타입 미제공 DB 사용)

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
@Entity
@Table(name = "MESSAGE")
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Column(columnDefinition = "varchar(2000)") //type varchar 로 변경
    @Convert(converter = MapAndStringConverter.class) //converter class 등록
    private Map<String, Object> info;
}

 

Map 타입 컬럼에 아래 사항을 변경해주었다.

 

  • columnDefinition 변경 : JSON -> varchar(2000)
  • @Type(type="json") 삭제
  • @Convert(converter = {converter 구현체 이름}.class) 어노테이션 추가

이렇게 수정하면 문제 없이 매핑이 잘 된다! String<->Map 손쉽게 매핑 가능.

 

JSON <-> Map은 많은데 String<->Map 매핑 방법은 나와있지 않아서 해결한 방법을 정리해보았다!

 

 

728x90
profile

개발 공부 기록

@찐만두

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!