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

相关推荐
Leon-Ning Liu几秒前
MySQL数据恢复实践:binlog2sql数据追加
数据库·mysql
嵌入式-老费3 分钟前
esp32开发与应用(看门狗测试)
java·开发语言·数据库
czhc11400756633 分钟前
615:代码细节
数据库
知识分享小能手9 分钟前
Hadoop学习教程,从入门到精通, HBase 分布式数据库 — 完整知识点与案例代码(8)
数据库·hadoop·分布式
吴声子夜歌14 分钟前
SQL经典实例——处理数字
java·数据库·sql
NineData19 分钟前
日常巡检 Oracle 时,ChatDBA 怎么把会话、SQL 和等待事件一起看
数据库·sql·oracle·ninedata·故障排查·chatdba·实例巡检
海天一色y24 分钟前
深入理解 RAG 技术:从语义张量到向量数据库,Milvus 与 FAISS 全面对比
数据库·milvus·faiss
爱吃羊的老虎25 分钟前
【数据库】模块二:SQL 语句、高级特性与优化
数据库·oracle
Rain50925 分钟前
2.4. PostgreSQL 数据库连接与实战指南
前端·数据库·人工智能·后端·postgresql·数据分析
爱喝水的鱼丶31 分钟前
SAP-ABAP:SAP表与视图权限管控方案:表维护权限、视图访问权限配置实操
运维·数据库·性能优化·sap·abap·权限·表和视图