文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

mybatis 自动化处理 mysql 的json类型字段 终极方案

2023-09-10 16:45

关注

文章目录

why json

为何使用json格式存储

1 存储内容经常改变,需要新增或者删减字段,但是字段的删除可能会出错,字段的新增个数不确定(field命名没规律)
2 不想多存储字段的 父类字段 parent_id ,因为sql语法会很复杂
3 不想用其他数据库,比如mogoDB ,多引入框架,会增加复杂度
4 mysql 支持json,但是语法复杂; 借助 mybatis 即可实现 jsonString <==> java jsonObject 的双向操作

简介

本文基于原生的 mybatis ,而不是 mybatis-plus ,请知悉。
目标1-查询:查询数据库的json字段,转换为java的json对象,并优雅的返回前端
目标2-更新:识别前端的请求参数,转换为 数据库的 Json 字段 ,比如新增/更新
目标3-注解:不使用 xml增加 typeHandler,而是 使用注解方式
目标4-智能:不在sql中的字段上指定 typeHandler, 不要每次都手写,要 自动化识别

mysql 建表 json 字段,添加1条json 数据

-- 建表 json 字段,添加1条json 数据create table t_test_json(id int primary key auto_increment,json_field JSON  default null);insert into t_test_json( json_field) values ('{"hello":"world"}');

对应的java对象 JsonEntity

@Table(name="t_test_json")@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class JsonEntity{    @Id    private Integer id;    // 为何不是 ArrayNode 或者 ObjectNode ?     // 因为 JsonNode 是他们俩的父类,可以自动兼容2种格式的json : [{},{}] 和 {}     private JsonNode jsonField;        @SneakyThrows    @Override    public String toString() {        return JacksonUtils.writeValueAsString(this);    }}

mybatis,不使用 通用mapper

手动自定义1个类型处理器,专门处理 JsonNode 和Json 的互相转化

import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.SneakyThrows;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 org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;// @MappedTypes(JsonNode.class) // 因为BaseTypeHandler 泛型中指定了JsonNode 的话,这个注解也可以省略 @MappedJdbcTypes(value = JdbcType.VARCHAR, includeNullJdbcType = true)@Componentpublic class JsonNodeTypeHandler extends BaseTypeHandler<JsonNode> implements InitializingBean {    static JsonNodeTypeHandler j;    @Autowired    ObjectMapper objectMapper;        @Override    public void afterPropertiesSet() {        j = this; // 初始化静态实例        j.objectMapper = this.objectMapper; //及时拷贝引用    }    @Override    public void setNonNullParameter(PreparedStatement ps, int i, JsonNode jsonNode, JdbcType jdbcType) throws SQLException {        ps.setString(i, jsonNode != null ? jsonNode.toString() : null);    }    @SneakyThrows    @Override    public JsonNode getNullableResult(ResultSet rs, String colName) {        return read(rs.getString(colName));    }    @SneakyThrows    @Override    public JsonNode getNullableResult(ResultSet rs, int colIndex) {        return read(rs.getString(colIndex));    }    @SneakyThrows    @Override    public JsonNode getNullableResult(CallableStatement cs, int i) {        return read(cs.getString(i));    }    @SneakyThrows    private JsonNode read(String json) {        return json != null ? j.objectMapper.readTree(json) : null;    }}

将 自定义的类型处理器 加入到 mybatis 核心配置,不用 xml

public static SqlSessionFactory getSqlSessionFactory(DataSource dataSource, String javaEntityPath, String xmlMapperLocation) throws Exception {    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();    factoryBean.setDataSource(dataSource);    factoryBean.setTypeAliasesPackage(javaEntityPath);    //mybatis configuration    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();    // 下划线转驼峰    configuration.setMapUnderscoreToCamelCase(true);    // 返回Map类型时,数据库为空的字段也要返回  https://www.cnblogs.com/guo-xu/p/12548949.html    configuration.setCallSettersOnNulls(true);    // 配置 拦截器 打印 sql : TODO 补充 拦截器实现代码    // configuration.addInterceptor(new PrintMybatisSqlInterceptor());    factoryBean.setConfiguration(configuration);    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();    factoryBean.setMapperLocations(resolver.getResources(xmlMapperLocation));    // 自定义的类型处理器: 自动双向解析 JsonNode 类型 和 mysql中的 json; 千万别写xml 了,太low     factoryBean.setTypeHandlers(new JsonNodeTypeHandler());    return factoryBean.getObject();}@Beanpublic SqlSessionFactory yourSqlSessionFactory(DataSource yourDataSource) throws Exception {    return getSqlSessionFactory(yourDataSource,"com.server.model.entity.testpath","classpath:mapper/testpathprivate List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)

在这里插入图片描述

mybatis,使用 通用mapper

使用 通用mapper,可以少写很多单表操作的sql ,增删改查,单表操作非常方便

import tk.mybatis.mapper.annotation.ColumnType;@Table(name="t_test_json")@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class JsonEntity{    @Id    private Integer id;    // 多了这个ColumnType,通用mapper生成sql必须的;如果没有该注解,则最终生成sql时 该JsonNode类型的字段将会被忽略    // 如果你没有使用 通用mapper,而是完全手写sql,那么完全没必要加该注解,mybatis的自动发现足咦!!    @ColumnType(typeHandler = JsonNodeTypeHandler.class)     private JsonNode jsonField;        @SneakyThrows    @Override    public String toString() {        return JacksonUtils.writeValueAsString(this);    }}

最终效果展示 ,增删改查测试 代码示例 :

查询并显示 json

直接更新json

源代码下载

参考文档

  1. mysql 正确比较 2个json 字段 的写法: JSON_CONTAINS(json_field, #{vo.jsonField})

来源地址:https://blog.csdn.net/w1047667241/article/details/127697481

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯