当前位置: 首页 > news >正文

Mybatis(Plus)对JSON / Array类型进行序列化

本文目的: 实体类中对应JdbcType的json/array类型自动映射, 免去在各个位置声明typeHandler的困扰

依赖项说明:

springboot:3.5.19

  • org.mybatis:mybatis:3.5.19(mybatis plus也一样)
  • org.postgresql:postgresql:42.7.5(可改为MySQL)
  • org.reflections:reflections:0.10.2

前言

对于各种带有泛型的java类型, 由于mybatis的typeHandler对泛型非常不友好, 所以一般情况下无法解析

因此我们首先要做的就是消除泛型, 然后再解析, 解析逻辑就会简单多了

MySQL或者其他数据库也都一样, 就不详细说明了

声明类型接口, 用于消除泛型

  1. Postgresql JSON类型接口

    public interface PgJsonType {
    }
    
  2. Postgresql Array类型接口(仅支持基本类型, 如long, boolean, string, decimal等, 不支持多维数组和组合类型)

    public interface PgArrayType<T> extends Collection<T> {JdbcType getJdbcType();
    }
    

示例json类型声明

消除泛型的做法, 就是讲带有泛型的类, 重新声明为一个新的类型, 如:

假设有对象:

@Data
class Demo {private String name;private Integer age;
}

json类型示例为:

{name: "张三", age: 10}

或json数组类型:

[{name: "张三", age: 10}]
  1. 则为json集合类型创建类

    public class DemoJsonList extends ArrayList<Demo> implements PgJsonType {
    }
    
  2. 为json类型创建类

    public class DemoJson implements PgJsonType {
    }
    

此时实体类中不再使用List<Demo>或者Demo来声明字段, 而是使用下面的方式:

@Data
class SomeEntity {private DemoJson demoJson;private DemoJsonList demoJsonList;
}

示例Array类型

  1. 字符串数组

    public class StrList extends ArrayList<String> implements PgArrayType<String> {@Overridepublic JdbcType getJdbcType() {return JdbcType.VARCHAR;}
    }
    
  2. Long数组

    public class LongList extends ArrayList<Long> implements PgArrayType<Long> {@Overridepublic JdbcType getJdbcType() {return JdbcType.NUMERIC;}
    }
    

为数组和json类型创建typeHandler

  1. 先创建一个SpringContextUtil

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;@Component
    public class SpringContextUtil implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}public static <T> T getBean(Class<T> requiredType) {return context.getBean(requiredType);}
    }
    
  2. 然后创建包扫描工具类

    import org.reflections.Reflections;import java.util.Set;/*** 类型扫描相关工具类*/
    public class TypeScanUtil {/*** 子类型扫描** @param prefix 包前缀* @param clazz  目标类型* @return 目标类型的子类型*/public static <T> Set<Class<? extends T>> scanSubTypesOf(String prefix, Class<T> clazz) {Reflections reflections = new Reflections(prefix);return reflections.getSubTypesOf(clazz);}
    
  3. PgArrayTypeHandler.java(用于处理array类型)

    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.RequiredArgsConstructor;
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    import org.springframework.beans.BeanUtils;
    import org.springframework.core.GenericTypeResolver;import java.sql.*;
    import java.util.Objects;@RequiredArgsConstructor
    public class PgArrayTypeHandler<T extends PgArrayType<V>, V> extends BaseTypeHandler<T> {private final Class<T> clazz;@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, PgArrayType parameter, JdbcType jdbcType) throws SQLException {Array array = ps.getConnection().createArrayOf(parameter.getJdbcType().name(), parameter.toArray());ps.setArray(i, array);array.free();}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {return extractArray(rs.getArray(columnName));}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return extractArray(rs.getArray(columnIndex));}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return extractArray(cs.getArray(columnIndex));}protected T extractArray(Array array) throws SQLException {if (Objects.isNull(array)) {return null;}Object[] javaArray = (Object[]) array.getArray();array.free();if (Objects.isNull(javaArray)) {return null;}ObjectMapper objectMapper = SpringContextUtil.getBean(ObjectMapper.class);// noinspection uncheckedClass<V> type = (Class<V>) GenericTypeResolver.resolveTypeArgument(clazz, PgArrayType.class);T result = BeanUtils.instantiateClass(clazz);for (Object o : javaArray) {V value = objectMapper.convertValue(o, type);result.add(value);}return result;}
    }
    
  4. PgJsonTypeHandler.java(用于处理json类型)

如果是MySQL, setNonNullParameter方法声明为:

ps.setString(i, toJson(parameter));

另外如果pg指定了jdbc参数stringtype=unspecified时, 也可以用上述指令, 这样就可以同时支持json和jsonb了

指定stringtype示例:

jdbc:postgresql:///db?stringtype=unspecified
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;@RequiredArgsConstructor
public class PgJsonTypeHandler<T extends PgJsonType> extends BaseTypeHandler<T> {private final Class<T> clazz;@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {PGobject json = new PGobject();json.setType("jsonb");json.setValue(toJson(parameter));ps.setObject(i, json);}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {final String json = rs.getString(columnName);return StringUtils.isBlank(json) ? null : parse(json);}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {final String json = rs.getString(columnIndex);return StringUtils.isBlank(json) ? null : parse(json);}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {final String json = cs.getString(columnIndex);return StringUtils.isBlank(json) ? null : parse(json);}@SneakyThrows(JsonProcessingException.class)public T parse(String json) {return SpringContextUtil.getBean(ObjectMapper.class).readValue(json, clazz);}@SneakyThrows(JsonProcessingException.class)public String toJson(T obj) {return SpringContextUtil.getBean(ObjectMapper.class).writeValueAsString(obj);}
}
  1. 注册类型转换器

    其中kim.nzxy.mybatis.type.xxx替换成StrList等类所在的包即可

    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import lombok.RequiredArgsConstructor;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;import java.time.LocalDateTime;
    import java.util.Set;@Component
    @RequiredArgsConstructor
    @Configuration
    public class MybatisConfig {@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public CommandLineRunner registerTypes(SqlSessionFactory sqlSessionFactory) {return args -> {TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();// noinspection rawtypesSet<Class<? extends PgArrayType>> pgArrayTypes = TypeScanUtil.scanSubTypesOf("kim.nzxy.mybatis.type.xxx", PgArrayType.class);pgArrayTypes.forEach(it -> typeHandlerRegistry.register(it, PgArrayTypeHandler.class));Set<Class<? extends PgJsonType>> pgJsonTypes = TypeScanUtil.scanSubTypesOf("kim.nzxy.mybatis.type.xxx", PgJsonType.class);pgJsonTypes.forEach(it -> typeHandlerRegistry.register(it, PgJsonTypeHandler.class));};}
    }
  2. 完结撒花

http://www.xdnf.cn/news/718687.html

相关文章:

  • 2.环境搭建
  • 项目更改权限后都被git标记为改变,怎么去除
  • ai绘制mg人物半侧面头型
  • 2025年OE SCI2区TOP,进化麻雀搜索算法ESSA+海洋阻尼器迟滞建模与辨识,深度解析+性能实测
  • 进程同步机制-信号量机制-记录型信号量机制中的的wait和signal操作
  • 未来楼宇自控系统升级优化,为绿色建筑发展注入更强动力支撑
  • 大模型应用开发之评估
  • 浅谈学习(费曼学习法)
  • 四叉树实现四边形网格
  • OpenGL —— 2.9.1、摄像机之模拟CS鼠标视角转动,可切换线框模式显示(附源码,glfw+glad)
  • 力扣刷题Day 64:括号生成(22)
  • 什么是物化视图(Materialized View)?
  • Redis的大Key问题如何解决?
  • [预训练]Encoder-only架构的预训练任务核心机制
  • 【Day39】
  • 【Doris基础】Apache Doris数据模型全面解析:选择最适合你的数据组织方式
  • ProfibusDP转DeviceNet协议转换网关应用于S7-300PLC控制埃斯顿DeviceNet焊机项目
  • unity—特效闪光衣服的设置
  • 亚马逊桌布运营中的利润核算与优化:从成本管控到决策升级
  • MonoPCC:用于内窥镜图像单目深度估计的光度不变循环约束|文献速递-深度学习医疗AI最新文献
  • 5.3.1_2二叉树的层次遍历
  • 博客摘录「 Activiti7工作流引擎:基础篇(二) 自动生成表结构」2024年9月13日
  • 更换Homebrew 源
  • 【C/C++】闭包的几个用处
  • 如何用Go创建一个 deployment 到容器拉起来的全流程
  • python 制作复杂表格报告
  • Java 开发上门家政系统源码:全流程数字化管理,适配家政公司 / 个体户接单派单
  • MTK平台-- wifi 暗屏待机 low power问题分析
  • 自增长主键的优缺点分析
  • 20中数组去重的方法20种数组去重的方法