一、 数据库类型
二、使用Hutool工具
存储时将数据转换为JSON数据
获取时将JSON数据转换为对象
发现问题:
原本数据对象是Address
和 Firend
但是转换完成后数据变成了JSONArray和JSONObject
三、自定义TypeHandler继承Mybatis的BaseTypeHandler处理器
package com.jiusi.config;
import cn.hutool.json.JSONUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class jsonTypeHandler01<T> extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
//将数据转换为json字符串
ps.setString(i, JSONUtil.toJsonStr(parameter));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
//将json字符串转换为任意类型
if(rs.getString(columnName).charAt(0)=='{'){
return (T) JSONUtil.parseObj(rs.getString(columnName));
}
return (T)JSONUtil.parseArray(rs.getString(columnName));
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
在xml中使用
添加数据时在需要改变的字段后添加TypeHandler属性,使用自定义的JsonTypeHandler
<insert id="insert">
insert into msg (name, age, height, address, friend)
values (#{name}, #{age}, #{height}, #{address,typeHandler=com.jiusi.config.jsonTypeHandler},
#{friend,typeHandler=com.jiusi.config.jsonTypeHandler})
</insert>
获取数据时在ResultMap里Result上添加typeHandler属性,同样使用自定义的JackonTypeHandler
<resultMap type="com.jiusi.model.Msg" id="Jackon">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="height" column="height"></result>
<result property="address" column="address" typeHandler="com.jiusi.config.JackonTypeHandler"> </result>
<result property="friend" column="friend" typeHandler="com.jiusi.config.JackonTypeHandler"></result>
</resultMap>
但是查询出来的同样是JSONArray和HJSONObject
为什么使用BaseTypeHandler 而不是 TypeHandler
BaseTypeHandler<T>
是一个实现了TypeHandler
接口的抽象基类,提供了对TypeHandler
接口的一些基础实现和默认行为,简化了自定义类型处理器的开发。通过继承BaseTypeHandler
,开发者只需要关注具体的转换逻辑,而无需重复实现所有接口方法。
四、结合Redis进行转换
往数据库存储数据的同时将全类名也存入进去
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
package com.jiusi.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class jsonTypeHandler02<T> extends BaseTypeHandler<T> {
Jackson2JsonRedisSerializer<Object> serializer;
// 解决序列化乱码问题
public jsonTypeHandler02() {
// 指定序列化输入的类型, 即输入到redis的类型
serializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 指定序列化输出的类型
ObjectMapper objectMapper = new ObjectMapper();
//JsonAutoDetect.Visibility.ANY 代表所有属性或字段都可以序列化
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//新版用法
//以数组的方式存放到Redis,Class Type 全类名作为为第一个元素,Json字符串为第二个元素。
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
//将参数序列化为byts数组然后转成字符串
ps.setString(i,new String( serializer.serialize(parameter)));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 将字符串反序列化为对象
return (T) serializer.deserialize(rs.getString(columnName).getBytes(StandardCharsets.UTF_8));
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
将xml中的typeHandler 修改成当前处理器
五、自定义序列化方式
与Redis一样将全类名同时存入数据库
package com.jiusi.config;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 自定义序列化方式
* @param <T>
*/
public class MybatisToJsonConfig<T> {
static {
DEFAULT_CHARSET = StandardCharsets.UTF_8;
}
public static final Charset DEFAULT_CHARSET;
private final JavaType javaType;
private ObjectMapper objectMapper = new ObjectMapper();
public MybatisToJsonConfig(Class<T> type) {
this.javaType = this.getJavaType(type);
}
public MybatisToJsonConfig(JavaType javaType) {
this.javaType = javaType;
}
public T deserialize(@Nullable byte[] bytes) throws Exception {
try {
return this.objectMapper.readValue(bytes, 0, bytes.length, this.javaType);
} catch (Exception ex) {
throw new Exception("无法读取JSON: " + ex.getMessage(), ex);
}
}
public byte[] serialize(@Nullable Object t) throws Exception {
try {
return this.objectMapper.writeValueAsBytes(t);
} catch (Exception ex) {
throw new Exception("无法写入JSON: " + ex.getMessage(), ex);
}
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' 不能为空");
this.objectMapper = objectMapper;
}
private JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
package com.jiusi.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 同样继承BaseTypeHandler但是序列化方式是使用自定义序列化方式
* @param <T>
*/
public class JsonTypeHandler03<T> extends BaseTypeHandler<T> {
private static MybatisToJsonConfig<Object> serializer;
static {
serializer = new MybatisToJsonConfig<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
//JsonAutoDetect.Visibility.ANY 代表所有属性或字段都可以序列化
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//以数组的方式存放到Redis,Class Type 全类名作为为第一个元素,Json字符串为第二个元素。
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
//使用Redis的序列化将参数序列化为byts数组然后转成字符串
try {
ps.setString(i,new String( serializer.serialize(parameter)));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 是使用自定义的Redis反序列化将字符串反序列化为对象
try {
return (T) serializer.deserialize(rs.getString(columnName).getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
将xml中的typeHandler 修改成当前处理器
六、Mybatis-Plus
使用Mybatis-Plus同样可以只需要只需要加两个注解在类上添加@TableName(Value = “表名” , autoResultMap = true),并且在需要转换的字段上添加@TableField( typeHandler = 处理器)
处理器可以使用上面我们自定义的处理器