MyBatis Plus:自定义typeHandler类型处理器

目录

引言:关于TypeHandler

PostGreSQL:JSON数据类型

PostGreSQL数据库驱动:PGobject类

TypeHandler类型处理器

自定义类型处理器

类型处理器实现:PGJsonTypeHandler

注册类型处理器


引言:关于TypeHandler

MyBatis Plus提供了丰富的类型处理器TypeHandler,用于实现用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值。

但是,内置的类型处理器在某些特殊情况难以满足实际开发需求。以下,以PostGreSQL数据库中的JSON类型为例,叙述自定义类型处理器,实现java.util.Map与PostGreSQL中JSON类型之间的转换。

PostGreSQL:JSON数据类型

使用SQL语句构建JSON类型字段值是十分简单的,

sql 复制代码
-- 简单标量/基本值
-- 基本值可以是数字、带引号的字符串、true、false或者null
SELECT '5'::json;

-- 有零个或者更多元素的数组(元素不需要为同一类型)
SELECT '[1, 2, "foo", null]'::json;

-- 包含键值对的对象
-- 注意对象键必须总是带引号的字符串
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;

-- 数组和对象可以被任意嵌套
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;

问题在于:如何将java中的Map类型转换为PG数据库中的JSON字段值。

PostGreSQL数据库驱动:PGobject类

在PostGreSQL数据库驱动依赖包的org.postgresql.util工具包下,挖出来了一个名称为:PGobject的类,

该类(PGobject)的源码描述信息如下:

PGobject is a class used to describe unknown types An unknown type is any type that is unknown by JDBC Standards.

概略含义:PGobject是一个用于描述一种非JDBC标准的未知类型。

可以看到,它内置了很多子类,用于描述PostGreSQL中的Line、Polygon、Money等数据类型。

一通分析之后,发现PGobject类的type、value属性是比较重要的(毕竟只提供了这两个属性),

查看源码之后,解读type和value字段的含义:

type:object对象的类型,

value:object对象的值。

再拿来和PGpolygon子类做一下对比,会发现:type属性其实就是一个String字符串,用于描述PostGreSQL内置数据类型的名称;value则是具体的值。那么,我们也可以借助这个PGobject类来描述自己的类型。

TypeHandler类型处理器

TypeHandler类型处理器是MyBatis/MyBatisPlus很重要的一部分内容,日常开发中,从数据表到JavaBean,从JavaBean到数据表,两者之间的转换全靠这些内置的TypeHandler来实现。

查看TypeHandler的源码,内容如下,

java 复制代码
/**
 * @author Clinton Begin
 */
public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * Gets the result.
   *
   * @param rs
   *          the rs
   * @param columnName
   *          Column name, when configuration <code>useColumnLabel</code> is <code>false</code>
   * @return the result
   * @throws SQLException
   *           the SQL exception
   */
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

可以分为两类,其中:

①setParameter:是将JavaBean属性值转换为PostGreSQL支持的数据库字段值;

②3个getResult方法:熟悉原生JDBC编程的朋友应该都很眼熟,其实就是通过PreparedStatement执行完SQL之后,对ResultSet结果集进行按字段名columnName、按索引下标columnIndex,将PostGreSQL支持的数据库字段值,转换为JavaBean属性值的过程。

自定义类型处理器

了解了上述内容之后,我们要自定义类型处理器,核心的工作就是:对TypeHandler接口提供的三个方式进行实现。

类型处理器实现:PGJsonTypeHandler

以下给出一个Map集合类型到PostGreSQL-JSON数据类型之间的转换器实现类,相关部分可以直接查看代码注释,

java 复制代码
/**
 * TypeHandler:类型处理器,用于 JavaType 与 JdbcType 之间的转换
 *  [1] 用于 PreparedStatement 设置参数值
 *  [2] 用于从 ResultSet CallableStatement 中取出一个值
 * PGJsonTypeHandler:处理Map集合类型与postgresql中JSON类型之间的转换
 */

@MappedTypes(value = {Object.class})
public class PGJsonTypeHandler<T> extends BaseTypeHandler<T> {

    //PGobject:PGobject is a class used to describe unknown types An unknown type is any type that is unknown by JDBC Standards.
    private static final PGobject pgObject = new PGobject();

    /**
     * 插入时设置参数类型
     * @param preparedStatement SQL预编译对象
     * @param i 需要赋值的索引位置(相当于在JDBC中对占位符的位置进行赋值)
     * @param parameter 索引位置i需要赋的值(原本要给这个位置赋的值,在setNonNullParameter方法中主要解决的问题就是将这个自定义类型变成数据库认识的类型)
     * @param jdbcType jdbc的类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (preparedStatement != null) {
            System.out.println("setNonNullParameter::"+parameter.toString());
            pgObject.setType("json");
            pgObject.setValue(JSON.toJSONString(parameter));
            preparedStatement.setObject(i, pgObject);
        }
    }

    /**
     * 转换为T类型实例
     * @param object 参数字符串({"name":"张三","age":18,"address":"北京市朝阳区"})或者null空值
     * @return T实例或者参数
     */
    private T parseAsTInstance(Object object){
        //判空
        if(object == null){
            return null;
        }
        //处理字符串{"name":"张三","age":18,"address":"北京市朝阳区"}-转换为Map类型
        Map map = JSON.parseObject(object.toString(), Map.class);
        return (T)map;
    }

    /**
     * 获取时转换回的自定义类型
     * @param resultSet 结果集
     * @param s 列名称
     * @return Bean对象
     */
    @Override
    public T getNullableResult(ResultSet resultSet, String s) throws SQLException {
        Object object = resultSet.getObject(s);
        return parseAsTInstance(object);
    }

    /**
     * 获取时转换回的自定义类型
     * @param resultSet 结果集
     * @param i 列索引
     */
    @Override
    public T getNullableResult(ResultSet resultSet, int i) throws SQLException {
        Object object = resultSet.getObject(i);
        return parseAsTInstance(object);
    }

    /**
     * 获取时转换回的自定义类型
     * @param callableStatement 结果集
     * @param i 列索引
     */
    @Override
    public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        Object object = callableStatement.getObject(i);
        return parseAsTInstance(object);
    }
}

注册类型处理器

要注册类型处理器,可以进行全局配置:typeHandlersPackage属性

也可以在局部直接使用,例如:

java 复制代码
package com.example.soil_backend.model.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;

import java.time.LocalDateTime;

import com.baomidou.mybatisplus.annotation.TableId;

import java.io.Serializable;
import java.util.Map;

import com.example.soil_backend.model.TbBase;
import com.example.soil_backend.typehandler.PGJsonbTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * 配置模板
 *
 * @author XiMumu
 * @since 2024-02-18
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName(value = "tb_template", autoResultMap = true)
public class TbTemplate extends TbBase implements Serializable {
    private static final long serialVersionUID = -2288822970791637243L;
    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 调查模板名称
     */
    @TableField(value = "name")
    private String name;

    /**
     * 模板配置JSON
     */
    @TableField(value = "config",typeHandler = PGJsonbTypeHandler.class)
    private Map config;

    /**
     * 是否删除:1-使用中;2-已删除
     */
    @TableField(value = "del_flag")
    private Integer delFlag;


}
相关推荐
鹿屿二向箔3 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis
沐雪架构师6 小时前
mybatis连接PGSQL中对于json和jsonb的处理
json·mybatis
鹿屿二向箔7 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的咖啡馆管理系统
spring·mvc·mybatis
aloha_78917 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
毕业设计制作和分享18 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
paopaokaka_luck21 小时前
基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)
java·spring boot·小程序·毕业设计·mybatis·1024程序员节
cooldream20091 天前
Spring Boot中集成MyBatis操作数据库详细教程
java·数据库·spring boot·mybatis
不像程序员的程序媛1 天前
mybatisgenerator生成mapper时报错
maven·mybatis
小布布的不1 天前
MyBatis 返回 Map 或 List<Map>时,时间类型数据,默认为LocalDateTime,响应给前端默认含有‘T‘字符
前端·mybatis·springboot
背水1 天前
Mybatis基于注解的关系查询
mybatis