스프링 myBatis JSON 형식 입력하고 출력하기 : Spring myBatis JSONType input output  

스프링 myBatis JSON 형식 입력하고 출력하기 : Spring myBatis JSONType input output

개발환경

Java 7.x
Spring 4.x
myBatis 3.2.8
jackson 2.0.1
H2 1.4.180

myBatis 를 이용한 JSON 형식의 데이터를 저장하고 조회하는 로직을 간단하게 구현하는 방법을 설명하려고 한다.

데이터의 흐름

저장: [클라이언트] --JSON--> [Spring] --String JSON--> [DB]
조회: [DB] --String JSON--> [Spring] --Object JSON--> [클라이언트]

데이터의 흐름은 클라이언트에서 전송된 json 형식의 데이터를 서버 DB에 저장하고 다시 클라이언트로 json 데이터를 넘겨주는 작업인데... 흔히 json 데이터를 문자열로 변경하고 db에 저장한 후 출력할때는 다시 문자열을 객체로 변경하고 프론트엔드로 넘겨주는 식이다. 이렇게하면 domain의 데이터 형식이 문자열이여야하나 객체여야하나 혼동이 생길수도 있고 바인딩 과정이 복잡해지는 문제가 발생한다.

저장: [클라이언트] --JSON--> [Spring] --Object JSON--> [DB: JSON Parsing]
조회: [DB: JSON Parsing] --Object JSON--> [Spring] --JSON--> [클라이언트]

그래서 위의 데이터 흐름과 같이 JSON 파싱작업은 myBatis에서 하고 json 데이터 형식은 항상 객체로 유지하도록하여 심플한 로직을 구현할 수 있게 하였다.

로직 구현

테이블

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

CREATE TABLE ex (
  name VARCHAR(10),
  json  VARCHAR(4000)
);

도메인 클래스

package org.syaku.ex.domain;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.lang.builder.ToStringBuilder;

import java.util.Map;

/**
 * @author Seok Kyun. Choi. 최석균 (Syaku)
 * @site http://syaku.tistory.com
 * @since 16. 3. 7.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class Ex {
    private String name;
    private Map<String, Object> json;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Map<String, Object> getJson() {
        return json;
    }

    public void setJson(Map<String, Object> json) {
        this.json = json;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

myBatis에서 지원하는 타입헨들러를 이용하면 쉽게 구현이 가능하다. JacksonParsing 클래스는 jackson 라이브러리를 이용하여 json 형식을 간편하게 파싱하기 위해 따로 만든 클래스이다.

package org.syaku.ex.mapper.type;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.syaku.common.object.JacksonParsing;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author Seok Kyun. Choi. 최석균 (Syaku)
 * @site http://syaku.tistory.com
 * @since 16. 3. 8.
 */
public class JSONTypeHandler extends BaseTypeHandler<Object> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
            throws SQLException {

        String p = JacksonParsing.toString(parameter);
        ps.setObject(i, p);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        Object d = rs.getObject(columnName);
        if(d == null) return d;
        return JacksonParsing.toMap(d.toString());
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        Object d = rs.getObject(columnIndex);
        if(d == null) return d;
        return JacksonParsing.toMap(d.toString());
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        Object d = cs.getObject(columnIndex);
        if(d == null) return d;
        return JacksonParsing.toMap(d.toString());
    }
}
  • setNonNullParameter: 저장할때 데이터 파싱
  • getNullableResult, getNullableResult, getNullableResult : 조회할때 데이터 파싱

만들어지 타입헨들러를 맵퍼에 적용해보자.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.syaku.ex.mapper.ExMapper">

    <resultMap id="ex" type="org.syaku.ex.domain.Ex">
        <result property="name" column="name" />
        <result property="json" column="json" typeHandler="org.syaku.ex.mapper.type.JSONTypeHandler" />
    </resultMap>

    <select id="select" resultMap="ex">
        SELECT * FROM ex
    </select>

    <insert id="insert">
        INSERT INTO ex (
            name,
            json
        ) VALUES (
            #{name},
            #{json, typeHandler=org.syaku.ex.mapper.type.JSONTypeHandler}
        )
    </insert>

</mapper>

typeHandler 속성에 org.syaku.ex.mapper.type.JSONTypeHandler 패키지를 포함한 클래스명을 입력하면 된다.

테스트

package org.syaku.ex.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.syaku.ex.domain.Ex;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Seok Kyun. Choi. 최석균 (Syaku)
 * @site http://syaku.tistory.com
 * @since 16. 3. 8.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:config/*-context.xml" })
public class ExServiceTest {

    @Resource(name = "exService") ExService exService;

    @Test
    public void select() {
        Ex ex = new Ex();
        ex.setName("syaku");

        Map<String, Object> json = new HashMap<>();
        json.put("name","syaku");
        json.put("value","spring");
        ex.setJson(json);

        exService.insert(ex);
        exService.insert(ex);
        exService.insert(ex);
        exService.insert(ex);
        exService.insert(ex);

        for(Ex data : exService.getExList()) {
            System.out.println(data.toString());
        }

    }
}

결과

org.syaku.ex.domain.Ex@2f7f2ae[name=syaku,json={name=syaku, value=spring}]
org.syaku.ex.domain.Ex@645d7f07[name=syaku,json={name=syaku, value=spring}]
org.syaku.ex.domain.Ex@363ed80b[name=syaku,json={name=syaku, value=spring}]
org.syaku.ex.domain.Ex@57d9d5ff[name=syaku,json={name=syaku, value=spring}]
org.syaku.ex.domain.Ex@1e708568[name=syaku,json={name=syaku, value=spring}]

Github

https://github.com/syakuis/spring-syaku/tree/mybatis-ex1



출처: https://syaku.tistory.com/311 [개발자 샤쿠 (Syaku)]

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
49 org.eclipse.ui.navigator Class CommonViewer 졸리운_곰 2019.07.04 398
48 MessageBox Example : Dialog « SWT JFace Eclipse « Java file 졸리운_곰 2019.06.16 362
47 Add Table Selection Listener and Get Selected TableItem : Table Event « SWT « Java Tutorial file 졸리운_곰 2019.06.16 372
46 Building and delivering a table editor with SWT/JFace file 졸리운_곰 2019.06.16 430
45 Demonstrates CellEditors : Table « SWT JFace Eclipse « Java file 졸리운_곰 2019.06.16 279
44 Demonstrates TableViewers : Table « SWT JFace Eclipse « Java file 졸리운_곰 2019.06.16 323
43 Eclipse RCP Tutorial: How to Add a Progress Bar file 졸리운_곰 2015.11.07 457
42 SWT Custom Widgets - Tutorial file 졸리운_곰 2015.08.23 379
41 Show a tool tip inside a rectangle : ToolTip « SWT « Java Tutorial file 졸리운_곰 2015.08.22 285
40 [SWT, Java] Tooltip example 졸리운_곰 2015.08.22 270
39 [SWT, Java], Button by Image, 이미지로 버튼 생성 졸리운_곰 2015.08.22 416
38 [SWT] Image Button 졸리운_곰 2015.08.22 277
37 [SWT] eventListener에서 부모 class (이벤트발생 클래스) 얻기 졸리운_곰 2015.08.16 340
36 [SWT] MessageBox Example file 졸리운_곰 2015.08.16 494
35 [SWT] How to create your own dialog classes file 졸리운_곰 2015.08.16 347
34 [SWT] Number Input Dialog file 졸리운_곰 2015.08.16 275
33 [SWT] Demonstrates a Canvas file 졸리운_곰 2015.08.12 268
32 SWT Control in One Example file 졸리운_곰 2015.08.10 403
31 SWT Tree With Multi columns file 졸리운_곰 2015.08.06 374
30 SWT Tree Composite 졸리운_곰 2015.08.06 289
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED