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

相关推荐
晓风残月淡2 小时前
高性能MYSQL(四):查询性能优化
数据库·mysql·性能优化
天若有情6732 小时前
用MySQL+BI工具搭建企业级数据可视化看板:从数据准备到动态展示全攻略
数据库·mysql·信息可视化
TDengine (老段)2 小时前
TDengine C# 语言连接器进阶指南
大数据·数据库·人工智能·物联网·c#·时序数据库·tdengine
FITA阿泽要努力2 小时前
动手体验:5min实现第一个智能体——1
json·api·agent·requests·查询天气·pprint·f-string
山峰哥2 小时前
SQL调优实战:让查询效率飙升10倍的降本密码
服务器·前端·数据库·sql·编辑器·深度优先
一个响当当的名号2 小时前
lectrue2 高级SQL
数据库·oracle
wtsolutions2 小时前
Using the Excel to JSON API - Programmatic Access for Developers
ui·json·xhtml
风叶悠然3 小时前
vue3中数据的pinia的使用
前端·javascript·数据库