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

news2025/1/17 21:39:44

目录

引言:关于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类型字段值是十分简单的,

-- 简单标量/基本值
-- 基本值可以是数字、带引号的字符串、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的源码,内容如下,

/**
 * @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数据类型之间的转换器实现类,相关部分可以直接查看代码注释,

/**
 * 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属性

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

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;


}

      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1458368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

普中51单片机学习(十四)

中断系统 中断的概念 CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;,CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务)&#xff0c;待CPU将事件B处理完毕后&#xff0c;再回到原来事件…

【C语言的小角落】逻辑与逻辑或混合计算

关注小庄 顿顿解馋(≧◡≦) 引言&#xff1a;本篇博客小庄带领小伙伴们解决一个比较角落有时头疼的问题—关于逻辑与和逻辑或结合运算的问题&#xff0c;请放心食用~ 我们先放代码说话 int main() {int x 1;int y 3;int z 4;if(x1 || y && z){;} printf("y …

【医学大模型】Text2MDT :从医学指南中,构建医学决策树

Text2MDT &#xff1a;从医学指南中&#xff0c;构建医学决策树 提出背景Text2MDT 逻辑Text2MDT 实现框架管道化框架端到端框架 效果 提出背景 论文&#xff1a;https://arxiv.org/pdf/2401.02034.pdf 代码&#xff1a;https://github.com/michael-wzhu/text2dt 假设我们有一…

使用Sora部署实时音视频通信应用实战项目

一、项目概述 本项目将构建一个在线教学平台&#xff0c;实现教师与学生之间的实时音视频通信。平台将提供教师上传课件、发起授课邀请&#xff0c;学生加入课堂、实时互动等功能。通过使用Sora&#xff0c;我们将确保音视频通信的稳定、流畅和低延迟。 目录 一、项目概述 二…

并发编程线程安全之同步锁Synchronized

一、原子性定义 原子性的本质是互斥访问&#xff0c;同一时刻只有一个线程对它进行访问操作 二、原子性问题的简述 public class AutomicDemo {int count 0;public static void main(String[] args) throws InterruptedException {AutomicDemo automicDemo new AutomicDem…

洛谷C++简单题小练习day15—计算阶乘小程序(不用循环)

day15--计算阶乘小程序--2.19 习题概述 题目描述 求 n!&#xff0c;也就是 123⋯n。 挑战&#xff1a;尝试不使用循环语句&#xff08;for、while&#xff09;完成这个任务。 输入格式 第一行输入一个正整数 n。 输出格式 输出一个正整数&#xff0c;表示 n! 代码部分 …

MySQL数据库基础(十一):多表查询

文章目录 多表查询 一、交叉连接&#xff08;了解&#xff09; 二、内连接 1、连接查询的介绍 2、内连接查询 3、小结 三、左外连接 1、左连接查询 2、小结 四、右外连接 1、右连接查询 2、小结 多表查询 一、交叉连接&#xff08;了解&#xff09; 它是所有连接…

Elasticsearch:将 IT 智能和业务 KPI 与 AI 连接起来 - 房间里的大象

作者&#xff1a;Fermi Fang 大象寓言的智慧 在信息技术和商业领导力的交叉点&#xff0c;蒙眼人和大象的古老寓言提供了一个富有洞察力的类比。 这个故事起源于印度次大陆&#xff0c;讲述了六个蒙住眼睛的人第一次遇到大象的故事。 每个人触摸大象的不同部位 —— 侧面、象牙…

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 二叉树简介二叉树分类解题方法制作不易&#xff0c;感谢三连&#xff0c;谢谢啦 二叉树简介 二叉树&#xff08;Binary Tree&#xff09;是一种特殊的树形…

UE蓝图 分支(Branch)节点和源码

系列文章目录 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 文章目录 系列文章目录一、分支节点功能二、分支节点用法三、分支节点使用场景四、分支节点实现过程五、分支节点相关源码 一、分支节点功能 在Unreal Engine&#xff08;UE&#xff09;的蓝图中&#xff0…

leetcode1049:最后一块石头的重量二

解题思路&#xff1a; 把石头堆分割成差不多的两堆&#xff0c;使得两堆差值最小 dp数组的含义&#xff1a; dp[j]:背包容量为j的背包最大重量(价值)为dp[j] dp[j] max(dp[j],dp[j-stones[i]] stones[i]) 初始化&#xff1a;&#xff08;dp数组的大小根据题目进行定义&a…

华为配置旁挂二层组网隧道转发示例

配置旁挂二层组网隧道转发示例 组网图形 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件扩展阅读 业务需求 企业用户通过WLAN接入网络&#xff0c;以满足移动办公的最基本需求。且在覆盖区域内移动发生漫游时&#xff0c;不影响用户的业务使用。 组网需求 AC组…

【总结】linux命令行在个人用户下添加环境变量

一&#xff0c;简介 在工作过程中需要将对应的路径添加到个人用户的环境变量中&#xff0c;本文介绍如何添加&#xff0c;供参考。 二&#xff0c;操作步骤 非常简单&#xff0c;只需要找到对应配置文件&#xff0c;将路径添加进去即可。 2.1 找到配置文件 一般为用户名下…

【无标题】力扣报错:member access within null pointer of type ‘struct ListNode‘

项目场景&#xff1a; 做单链表反转题目&#xff0c;报错&#xff1a;member access within null pointer of type ‘struct ListNode’ 题目链接:LINK 问题描述 我明明在初始化指针时候&#xff0c;已经处理了n2->next情况却依然报错 这个报错提示含义是&#xff1a;大概就…

react封装通用Modal弹窗组件

目录 1、【src/component/modal/hoc.js】 2、【src/component/modal/componentModal.js】 3、【src/page/projectView.js】 【说明】&#xff1a;后台管理的项目中会经常遇到弹窗&#xff0c;于是封装了一个简单的公共弹窗组件 这个公共组件不适用复杂的功能&#xff0c;简单的…

力扣题目训练(17)

2024年2月10日力扣题目训练 2024年2月10日力扣题目训练551. 学生出勤记录 I557. 反转字符串中的单词 III559. N 叉树的最大深度241. 为运算表达式设计优先级260. 只出现一次的数字 III126. 单词接龙 II 2024年2月10日力扣题目训练 2024年2月10日第十七天编程训练&#xff0c;今…

第五次作业(防御安全)

需求: 1.办公区设备可以通过电信链路和移动链路上网&#xff08;多对多的NAT&#xff0c;并且需要保留一个公网IP 不能用来转换&#xff09; 2.分公司设备可以通过总公司的移动链路和电信链路访问到DMZ区的http服务器 3.分公司内部的客户端可以通过公网地址访问到内部的服务…

力扣OJ题——相交链表

题目&#xff1a;160. 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 思路一&#xff08;暴力求解&#xff09;&#xff1a; A链表的每个节点依次跟B链表中节点进行…

xshell 修改时间破解使用

文章目录 xshell 修改时间破解使用:question: 问题:point_right: 解决方案&#xff1a; xshell 修改时间破解使用 参考地址&#xff1a;博客地址 ❓ 问题 问题 ​ 当前 xshell 官方提供免费的个人版本供使用&#xff0c;但是在官网升级新版本后则需要进行升级之后才能使用&…

调用接口时不时出现 Error: socket hang up

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 今天采用golang创建了一个http服务&#xff0c;准备对若干接口进行测试。 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 在测试第一个接口时&#xff0c;发现采用postman调用接口…