MyBatis TypeHandler๋ฅผ ์ด์šฉํ•œ ๊ฐ์ฒด๋ฆฌ์ŠคํŠธ ํ•ธ๋“ค๋ง

์ด๋ฒˆ์— ํŠน์ • ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋‹ค๊ฐ€ JSON ํ˜•ํƒœ์˜ ์ŠคํŠธ๋ง์„ ๊ทธ๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ–ˆ๋‹ค๊ฐ€ ๊บผ๋‚ด์จ์•ผํ•˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊ฒผ๋‹ค.

๊ทธ๊ฒƒ๋„ RBD์ธ mysql ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ง์ด๋‹ค. mysql์ด json ์ปฌ๋Ÿผ์„ ์ง€์›ํ•˜๊ธฐ๋Š” ํ•˜์ง€๋งŒ json ํ•จ์ˆ˜๋ฅผ ์ฟผ๋ฆฌ์— ์“ธ ํ•„์š”๊นŒ์ง€๋Š” ์—†๋Š” ์ƒํ™ฉ์ด๋‹ค๋ณด๋‹ˆ ๊ทธ๋ƒฅ varcharํƒ€์ž…์œผ๋กœ ์ปฌ๋Ÿผ์„ ์ •์˜ํ–ˆ๊ณ  ์—ฌ๊ธฐ์— jsonํฌ๋งท์˜ ์ŠคํŠธ๋ง์„ ๊ทธ๋Œ€๋กœ ์ €์žฅํ–ˆ๋‹ค๊ฐ€ ๊บผ๋‚ด์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ–ˆ๋‹ค.

์ผ๋‹จ ํƒ€์ž…ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๊ณ  ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด์ง€ ๊ฒ€์ƒ‰์„ ์ข€ ํ•ด๋ดค๋‹ค. ๊ตฌ๊ธ€์—์„œ "Type handler for ArrayList in myBatis" ๋ผ๊ณ  ๊ฒ€์ƒ‰์„ ํ•˜๋‹ˆ ๋งŽ์€ ํฌ์ŠคํŒ…์ด ๊ฒ€์ƒ‰์ด ๋˜์—ˆ๊ณ  ๋‚ด ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ธ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์ž‘์—… ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 

์šฐ์„  dto์˜ ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด MyJsonDataWrapperClass ๊ฐ€ myJsonData ๋ฆฌ์ŠคํŠธ๋ฅผ ๋“ค๊ณ  ์žˆ๋Š”๋ฐ ์ด๋•Œ MyJsonData ํด๋ž˜์Šค๊ฐ€ ๋ฐ”๋กœ json ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋  ์ •๋ณด์ด๋‹ค.

@Getter
@Setter
@JsonInclude(Include.NON_NULL)
public class MyJsonDataWrapperClass {

    private Integer someIntData;
    private List<MyJsonData> myJsonData = new ArrayList<>();
    
    @Override
    public String toString() {
        return new Gson().toJson(this);
    }
}
@Getter
@Setter
@EqualsAndHashCode
public class MyJsonData {
    private String name;
    private int age;

    @Override
    public String toString() {
        return new Gson().toJson(this);
    }
}

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” myJsonData ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ textํƒ€์ž…์˜ ์ปฌ๋Ÿผ์„ ๋งŒ๋“ค์—ˆ๊ณ  mybatis์˜ insert๋ฌธ์— ์‚ฌ์šฉํ•  ํƒ€์ž…ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ช…์‹œํ•ด์ฃผ์—ˆ๋‹ค.

, myJsonData = #{myJsonData, typeHandler=MyJsonDataTypeHandler}

 

๊ทธ๋ฆฌ๊ณ  ํƒ€์ž…ํ•ธ๋“ค๋Ÿฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ•˜์˜€๋‹ค.

@Slf4j
public class MyJsonDataTypeHandler extends BaseTypeHandler<List<MyJsonData>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<MyJsonData> parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, new Gson().toJson(parameter));
    }

    @Override
    public List<MyJsonData> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return convertToList(rs.getString(columnName));
    }

    @Override
    public List<MyJsonData> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return convertToList(rs.getString(columnIndex));
    }

    @Override
    public List<MyJsonData> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return convertToList(cs.getString(columnIndex));
    }

    private List<MyJsonData> convertToList(String myJsonDataListAsString) {
        try {
            return new ObjectMapper().readValue(myJsonDataListAsString, new TypeReference<List<MyJsonData>>() {
            });
        } catch (IOException e) {
            log.error("MyJsonDataTypeHandler failed to convert text to list, myJsonDataListAsString:{}", myJsonDataListAsString, e);
        }
        return Collections.emptyList();
    }
}

 

BaseTypeHandler๋ฅผ ์ƒ์†ํ•˜๋ฉด์„œ List<MyJsonData> ํƒ€์ž…์— ๋Œ€ํ•œ ํ•ธ๋“ค๋Ÿฌ์ž„์„ ๋ช…์‹œํ•ด์ฃผ์—ˆ๊ณ  setter์—๋Š” ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋ฅผ json ์ŠคํŠธ๋ง์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋„๋ก Gson.toJson() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์˜€๊ณ , getter์—๋Š” ์กฐํšŒํ•œ json ์ŠคํŠธ๋ง์„ ๋‹ค์‹œ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋„๋ก ํ•˜์˜€๋Š”๋ฐ ์ด๋•Œ๋Š” ObjectMapper.readValue()๋ฅผ ์ด์šฉํ•˜์˜€๋‹ค.

 

์กฐํšŒ์‹œ์—๋Š” mybatis ๋งคํ•‘ํŒŒ์ผ์— ์•„๋ž˜์™€ ๊ฐ™์ด resultMap์„ ์ •์˜ํ•˜์—ฌ MyJsonDataTypeHandler๋ฅผ ์ด์šฉํ•ด์„œ myJsonDataํ•„๋“œ์˜ ๊ฐ’์„ ArrayList๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ํ•˜์˜€์œผ๋ฉฐ, ์ด๋ ‡๊ฒŒ ์ •์˜ํ•œ resultMap์„ select ๋ฌธ์˜ resultMap์œผ๋กœ ์„ ์–ธํ•ด์ฃผ์—ˆ๋‹ค.

<resultMap id="myJsonDataClassMap" type="MyJsonDataWrapperClass">
<result property="myJsonData" column="myJsonData" javaType="java.util.ArrayList"
jdbcType="VARCHAR" typeHandler="MyJsonDataTypeHandler" />
</resultMap>

....์ค‘๋žต....

<select id="findMyJsonDataType"
resultMap="myJsonDataClassMap">
SELECT * FROM findMyJsonDataWrapperClass
</select>

 

์ด๋ ‡๊ฒŒ ํ•ด์ฃผ๋ฉด ์†Œ์Šค๋ ˆ๋ฒจ์—์„œ๋Š” ํŠน์ • ๊ฐ์ฒด์˜ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ํ•ธ๋“ค๋ง์„ ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” json ์ŠคํŠธ๋ง์œผ๋กœ ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ปฌ๋Ÿผ๋‹จ์œ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ชผ๊ฐœ์„œ ์ €์žฅํ•˜๊ธฐ ์• ๋งคํ•œ ์ƒํ™ฉ์—์„œ json ์ŠคํŠธ๋ง์„ varchar(text) ํƒ€์ž…์œผ๋กœ ํ†ต์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์†Œ์Šค๋ ˆ๋ฒจ์—์„œ๋Š” ๊ฐ์ฒดํƒ€์ž…์œผ๋กœ ํ•ธ๋“ค๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ฑด ์ •๋ง ๋ฉ‹์ง„ ๊ธฐ๋Šฅ์ธ๊ฒƒ ๊ฐ™๋‹ค.