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 对象自动互转了。

相关推荐
海兰3 小时前
【数据库】tdsql(mysql8.0)优化思考三
数据库·redis
煎饼学大模型4 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
CodeStats5 小时前
【Spring事务】Spring事务注解 @Transactional 完整体系:从 MySQL 隔离级别到 MyBatis 原理详解
java·spring·mybatis·事务·transactional
muddjsv8 小时前
SQLite数据库文件机制详解:journal、wal、shm文件作用与备份避坑
数据库·sqlite
吴声子夜歌9 小时前
MongoDB 4.2——应用程序设计
数据库·mongodb
数字新视界9 小时前
动环监控可视化在数据中心管理中的应用与实践解析
数据库·物联网·解决方案·动环监控·大榕树
极地野狼 音乐哔哔9 小时前
数据库备份恢复的原理实践
数据库
互联网中的一颗神经元9 小时前
小白python入门 - 25. SQL 与表设计入门
数据库·python·sql
ttwuai10 小时前
AI 生成后台改数据后,操作日志别只记按钮:Go + MySQL 怎么验
数据库·人工智能·mysql·golang
KaMeidebaby10 小时前
卡梅德生物技术快报 | 核酸适配体文库测序:核酸适配体文库测序的技术原理、实验流程与数据解析
前端·网络·数据库·人工智能·算法