背景
由于业务需要,存储一个Long类型的list在mysql里,使用的MySQL版本为8.0。查阅发现5.7以上已经支持json格式,想测试一下以json类型存储list。
准备
准备一个实体类映射到mysql表,实体类包括一个主键id以及一个Long类型的list。
public class MyEntity {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private List<Long> jsonList;
// 省略其他属性和方法
}
在数据库中新建表
CREATE TABLE my_entity (
id BIGINT PRIMARY KEY,
json_list JSON
);
新建typeHandler
要将 mysql中取出的JSON 类型数组转化为 Long 类型列表,需要自定义typeHandler。首先是json转list,假设命名为ListTypeHandler(json解析采用的是jackson):
package xxx.handler;
import xxx.JacksonUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.*;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes({List.class})
public abstract class ListTypeHandler<T> extends BaseTypeHandler<List<T>> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<T> ts, JdbcType jdbcType) throws SQLException {
String content = CollectionUtil.isEmpty(ts) ? null : this.toJSONString(ts);
preparedStatement.setString(i, content);
}
@Override
public List<T> getNullableResult(ResultSet resultSet, String s) throws SQLException {
try {
return this.getListByJsonArrayString(resultSet.getString(s));
} catch (JsonProcessingException e) {
throw new BizException(e);
}
}
@Override
public List<T> getNullableResult(ResultSet resultSet, int i) throws SQLException {
try {
return this.getListByJsonArrayString(resultSet.getString(i));
} catch (JsonProcessingException e) {
throw new BizException(e);
}
}
@Override
public List<T> getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
try {
return this.getListByJsonArrayString(callableStatement.getString(i));
} catch (JsonProcessingException e) {
throw new BizException(e);
}
}
private List<T> getListByJsonArrayString(String content) throws JsonProcessingException {
return StringUtils.isEmpty(content) ? new ArrayList<>() : new ObjectMapper().readValue(content, this.specificType());
}
protected abstract TypeReference<List<T>> specificType();
}
public String toJSONString(Object obj) {
try {
return null == obj ? null : mapper.writeValueAsString(obj);
} catch (JsonProcessingException var2) {
throw new BizException(var2);
}
}
再新建个LongTypeHandler类,将ListTypeHandler的元素设置为Long类型
package xxx.handler;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.List;
public class LongTypeHandler extends ListTypeHandler<Long>{
@Override
protected TypeReference<List<Long>> specificType() {
return new TypeReference<List<Long>>() {};
}
}
在mapper.xml中添加resultMap,对于json_list指定typeHandler,用于映射实体类时进行类型转换。指定将 JSON 格式的 Long 类型列表在数据库中的类型为 VARCHAR。
<resultMap id="MyEntity" type="xxx.MyEntity">
<id column="id" property="id"/>
<result column="json_list" property="jsonList"
jdbcType="VARCHAR"
typeHandler="xxx.LongTypeHandler"/>
</resultMap>
查阅的时候,resultMap加上这个id即可,比如:
<select id="xxx" resultMap="SqlInfoTabDO">
select
*
from
my_entity
如果是增删改的时候,需要加上typeHandler和jdbcType,这里用的是mybatis-plus的JacksonTypeHandler,比如
<insert id="insert" parameterType="xxx">
insert into my_entity
(json_list)
values
(
#{jsonList, jdbcType=VARCHAR,typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler}
)
</insert>
<update id="update" parameterType="xxx">
update
my_entity
set
json_list = #{json_list, jdbcType=VARCHAR, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler}
where id = #{id}
</update>
错误示范
刚开始的时候,本人是使用了JacksonTypeHandler对jsonList在取值的时候进行映射,如下:
<!-- 错误示范 -->
<resultMap id="MyEntity" type="xxx.MyEntity">
<id column="id" property="id"/>
<result column="json_list" property="jsonList"
jdbcType="VARCHAR"
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
</resultMap>
会出现什么情况呢?如果jsonList中long类型元素较小的时候,比如[1,2,3]等,从mysql中取出的时候,JacksonTypeHandler会将这些元素自动转换为“更适合”的Integer类型,如果像下面表达式一样取出:
List<Long> jsonList = mapper.getJsonList(id);
整个List都是Integer类型元素,转型为Long类型时会报错。
所以还是学会写typeHandler,将类型转换掌握在自己手中比较好。。。