存储或读取时转换JSON数据

发布于:2024-05-10 ⋅ 阅读:(18) ⋅ 点赞:(0)
一、 数据库类型

在这里插入图片描述

二、使用Hutool工具

存储时将数据转换为JSON数据

在这里插入图片描述

获取时将JSON数据转换为对象

在这里插入图片描述

发现问题:

原本数据对象是AddressFirend但是转换完成后数据变成了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 = 处理器)

处理器可以使用上面我们自定义的处理器

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到