MyBatis如何处理数据库中的JSON字段

MyBatis 没有办法像 MyBatis-Plus 一样无需用户介入自动完成数据库 JSON 字段到 Java 对象的转换,TA 需要借助 TypeHandler 机制。

那么 MyBatis 是如何借助 TypeHandler 实现 JSON 字段到 Java 对象转换的呢?

下面我将详细介绍这个过程。

首先,假设有一张名为 t_customer 的表,该表有一个 JSON 类型的字段 config ,如下:

sql 复制代码
CREATE TABLE `t_customer` (
  `id` int NOT NULL AUTO_INCREMENT,
  `code` varchar(50) NOT NULL COMMENT '客户编号',
  `category` varchar(15) NOT NULL COMMENT '客户类别',
  `config` json NOT NULL COMMENT '配置信息,例如:{"recharge": "off", "maxSpaceLimit": 10737418240}',  
  `create_time` bigint NOT NULL COMMENT '创建时间',
  `update_time` bigint NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `code_unique` (`code`)
)

与该表对应的实体类 Customer ,代码如下:

java 复制代码
@Data
@Entity
@Table(name = "t_customer")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 客户编号
     */
    private String code;

    /**
     * 客户类别
     */
    private String category;

    /**
     * 配置信息
     */		
    private Config config;

    /**
     * 创建时间
     */
    private Long createTime;

    /**
     * 更新时间
     */
    private Long updateTime;
    
    @Data
    public static class Config {
        /**
         * 是否开启充值功能,on:开启,off:关闭
         */
        private String recharge;

        /**
         * 最大空间限制,单位:B
         */
        private Long maxSpaceLimit;
    }
}

(注:实体类 Customer 中的 config 属性对应 t_customer 表中的 JSON 类型的 config 字段)

下面我们要为实体类 Customer 中的内部类 Customer.Config 创建一个与之对应的名为 CustomerConfigTypeHandler 的 TypeHandler,代码如下:

java 复制代码
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(Customer.Config.class)
public class CustomerConfigTypeHandler extends BaseTypeHandler<Customer.Config> {
    public CustomerConfigTypeHandler() {}

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Customer.Config parameter, JdbcType jdbcType) throws SQLException {
        try {
            String json = objectMapper.writeValueAsString(parameter);
            ps.setString(i, json);
        } catch (JsonProcessingException e) {
            throw new SQLException("Error converting Config to JSON", e);
        }
    }

    @Override
    public Customer.Config getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return parseJson(json);
    }

    @Override
    public Customer.Config getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String json = rs.getString(columnIndex);
        return parseJson(json);
    }

    @Override
    public Customer.Config getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String json = cs.getString(columnIndex);
        return parseJson(json);
    }

    private Customer.Config parseJson(String json) throws SQLException {
        if (json == null || json.isEmpty()) {
            return null;
        }
        try {
            return objectMapper.readValue(json, Customer.Config.class);
        } catch (Exception e) {
            throw new SQLException("Error parsing JSON to Config", e);
        }
    }
}

最后我们要对与 Customer 对应的 Mapper 接口(假设接口名是 CustomerMapper)进行修改,如下:

  • 查询
sql 复制代码
    @Select("SELECT * FROM t_customer WHERE code = #{code}")
    @Results({
            @Result(property = "config", column = "config", typeHandler = CustomerConfigTypeHandler.class)
    })
    Optional<Customer> selectByCode(@Param("code") String code);
  • 插入
sql 复制代码
    @Insert({
            "<script>",
            "INSERT INTO t_customer (
            	code, 
            	category, 
            	config, 
            	create_time, 
            	update_time
            ) ",
            "VALUES (
            	#{code}, 
            	#{category}, 
            	#{config, typeHandler=com.jnew.whatcell.dao.handler.CustomerConfigTypeHandler}), 
            	#{createTime}, 
            	#{updateTime}
            )
            "</script>"
    })
    int insert(Customer customer);
  • 更新
sql 复制代码
    @Update({
            "<script>",
            "UPDATE t_customer",
            "SET",
            "  config = #{config, typeHandler=com.cab5.xxx.dao.handler.CustomerConfigTypeHandler}",
            "WHERE code = #{code}",
            "</script>"
    })
    void updateConfigByCode(Customer customer);

至此,我们就可直接使用 Mapper 中更新后的 查询、插入、更新等方法,让 JSON 字段与 Java 对象自动互转了。

相关推荐
likangbinlxa3 小时前
【Oracle11g SQL详解】UPDATE 和 DELETE 操作的正确使用
数据库·sql
r i c k3 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦3 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL4 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·4 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德4 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫5 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i5 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.5 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn5 小时前
【Redis】渐进式遍历
数据库·redis·缓存