Chris Devlog

Mysql에서 Json type 활용하기(2) 본문

Spring Boot/JPA

Mysql에서 Json type 활용하기(2)

Chris Dev Heo 2024. 12. 31. 22:18
반응형

들어가며


이번 글에서는 Mysql에서 Json type 활용하기(1)에서 설정한 데이터를 기반으로 JPA와 연계하여 활용하는방법에 대해 이야기 해보겠습니다.

 

# 현재 테이블 구조

 

Enity 설정 Type


1. Map

필드 추가

  @JdbcTypeCode(SqlTypes.JSON)
  @Column(name = "json_data", columnDefinition = "json")
  private Map<String, Object> dataJson;

사용

 

  @Autowired
  JsonTest1Repository jsonTest1Repository;

  @Test
  void test2() throws JsonProcessingException {
    String json = "{\"key1\": \"value1\", \"key2\": 30000, \"key3\": \"2024-12-30 02:27:00\"}";

    ObjectMapper objectMapper = new ObjectMapper();
    Map<String, Object> map = objectMapper.readValue(json, Map.class);

    JsonTest1 jsonTest1 = jsonTest1Repository.save(JsonTest1.builder()
        .dataJson(map)
        .build());

    Assertions.assertEquals("value1", jsonTest1.getDataJson().get("key1"));
    Assertions.assertEquals(30000, jsonTest1.getDataJson().get("key2"));
    Assertions.assertEquals("2024-12-30 02:27:00", jsonTest1.getDataJson().get("key3"));

  }

장점

  • 맵으로 지정하면 json 데이터를 바로 맵에서 꺼내쓰기 좋음
  • 설정 및 사용이 간단함

단점

  • 저장시 Mapper을 이용해 Map으로 변환후 저장가능
  • 조회된 데이터를 활용하기 위해서는 추가적인 작업이 필요(형변환, DTO 맵핑 등)
  • Object Mapper, Gson등 Mapper사용시 LocalDateTime과 관련된 추가적인 설정이 필요(설정하지않으면 에러발생)
  • Map을 사용하기 때문에 디버깅이 어려움

2. String 

필드추가

  @JdbcTypeCode(SqlTypes.JSON) // JSON 타입 매핑
  @Column(name = "json_data", columnDefinition = "json")
  private String dataJson;

 

장점

  • 데이터 저장이 쉬움. 특히 벌크 인설트/업설트(유니크 데이터 존재시 업데이트, 없으면 인설트)인경우 코드가 현저히 줄어듬

단점

  • 필드 자체를 활용하기엔 어려움이 있음

 

Entity에 Map으로 선언하나 String으로 선언하나 사실 큰 차이는 없지만 굳이 String으로 선언하는 이유는 json 필드는 저장할 때만 사용하고 json 필드에서 값을 꺼내쓰지 않도록 하기 위해서 입니다

 

Jpa 환경에서 활용하기


 

Entity

@Getter
@Entity
@Table(name = "json_to_str")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
@DynamicInsert
public class JsonToStr {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Comment("json test id")
  @Column(name = "json_test_id", columnDefinition = "bigint")
  private Long id;

  @JdbcTypeCode(SqlTypes.JSON) // JSON 타입 매핑
  @Column(name = "json_data", columnDefinition = "json")
  private String jsonData;

  @Column(name = "key1", insertable = false, updatable = false)
  private String key1;

  @Column(name = "key2", insertable = false, updatable = false)
  private Long key2;

  @Column(name = "key3", insertable = false, updatable = false)
  private LocalDateTime key3;

  @CreationTimestamp
  @Column(name = "create_at", nullable = false, updatable = false, columnDefinition = "timestamp default current_timestamp")
  private LocalDateTime createAt;

  @Builder
  public JsonToStr(String dataJson) {
    this.dataJson = dataJson;
  }
}

 

이전글에서 json_test 테이블에 기본필드몇개를 추가하여 엔티티를 생성했습니다

기존 필드와의 차이점으로 json_data를 컬럼으로 등록한 경우 해당 컬럼은 "읽기전용"이기 때문에 필드에 insertable & updatable을 false 처리하여 save 메소드 호출시 json_data가 저장될 때 insert 쿼리에서 json_data만 저장 될 수 있도록 해줘야합니다

 

1. 아무 설정 없이 데이터 저장 후 데이터 확인

  @Autowired
  JsonToStrRepository jsonToStrRepository;

  @Test
  void test2() {
    String json = "{\"key1\": \"value1\", \"key2\": 30000, \"key3\": \"2024-12-30 02:27:00\"}";

    JsonToStr jsonToStr = jsonToStrRepository.save(JsonToStr.builder()
        .dataJson(json)
        .build());

    Assertions.assertEquals("value1", jsonToStr.getKey1());
    Assertions.assertEquals(30000, jsonToStr.getKey2());

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    Assertions.assertEquals(LocalDateTime.parse("2024-12-30 02:27:00", formatter), jsonToStr.getKey3());

  }
Execute DML : 

    insert 
    into
        json_to_str
        (create_at, json_data) 
    values
        ('2024-12-31T21:41:39.340+0900', cast('{"key1": "value1", "key2": 30000, "key3": "2024-12-30 02:27:00"}' as json))

Expected :value1
Actual   :null
<Click to see difference>

org.opentest4j.AssertionFailedError: expected: <value1> but was: <null>

 

영속성 컨텍스트에 캐시된 데이터는 jsonData밖에 없기 때문에 조회시 null일 수 밖에 없습니다

 

2. findById를 통해 데이터 리프래시

  @Autowired
  JsonToStrRepository jsonToStrRepository;

  @Test
  void test2() {
    String json = "{\"key1\": \"value1\", \"key2\": 30000, \"key3\": \"2024-12-30 02:27:00\"}";

    JsonToStr jsonToStr = jsonToStrRepository.save(JsonToStr.builder()
        .jsonData(json)
        .build());

    // Id를 통해 데이터를 다시한번 불러옴
    JsonToStr refreshJsonToStr = jsonToStrRepository.findById(jsonToStr.getId())
        .orElseThrow(RuntimeException::new);

    Assertions.assertEquals("value1", refreshJsonToStr.getKey1());
    Assertions.assertEquals(30000, refreshJsonToStr.getKey2());

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    Assertions.assertEquals(LocalDateTime.parse("2024-12-30 02:27:00", formatter), refreshJsonToStr.getKey3());

  }
Execute DML : 
    insert 
    into
        json_to_str
        (create_at, json_data) 
    values
        ('2024-12-31T22:07:17.462+0900', cast('{"key1": "value1", "key2": 30000, "key3": "2024-12-30 02:27:00"}' as json))

Execute DML : 
    select
        jts1_0.json_test_id,
        jts1_0.create_at,
        jts1_0.json_data,
        jts1_0.key1,
        jts1_0.key2,
        jts1_0.key3 
    from
        json_to_str jts1_0 
    where
        jts1_0.json_test_id=6

Tests passed: 1

 

데이터를 재 조회 해옴으로써 테스트에 통과하게 됩니다.

반응형

'Spring Boot > JPA' 카테고리의 다른 글

Mysql에서 Json type 활용하기(3)  (0) 2025.01.02
Mysql에서 Json type 활용하기(1)  (0) 2024.12.30