MyBatis自定义类型处理器 TypeHandler(通俗易懂,效率起飞),处理jsonb、数组、枚举类型。支持MybatisPlus

一 使用场景

在项目开发过程中,我们经常遇到这样的情况:Java 对象中的数据类型与数据库中的字段类型不一致。这时,我们需要在保存数据到数据库和从数据库检索数据时进行类型转换。例如:

  • 对于一些数据库特有的数据类型(如 PostgreSQL 的 jsonb 或数组类型),这些类型可能不被 MyBatis 默认支持,因此需要特殊处理。
  • 在 Java 实体(JavaBean)中,可能有一些字段是枚举(Enum)类型或特殊类型,而在数据库中,这些数据可能需要存储为字符串(String)或整数(Integer)。
  • 同样,你的 Java 实体可能有日期(Date)类型的字段,而在数据库中,相应的字段可能是以字符串(varchar)形式存储的日期。

这些类型不匹配的情况会导致大量的手动数据类型转换,这不仅麻烦,而且容易出错。为了解决这个问题,MyBatis 提供了一种功能强大的机制:TypeHandler 类型处理器。通过实现和使用类型处理器,我们可以自动化地进行数据类型转换,简化代码,提高开发效率。类型处理器使得 MyBatis 能够智能地处理那些它默认不支持的数据库字段类型,同时也方便了开发者在复杂数据类型和数据库类型之间进行无缝转换。

二 类型处理器 TypeHandler简介

在 MyBatis 中,TypeHandler(类型处理器)的主要作用是帮助我们在 Java 代码中使用的数据类型(JavaType)和数据库中的数据类型(JdbcType)之间进行转换。这就像是在 Java 世界和数据库世界之间搭建了一座桥梁。

  1. 当你需要把数据从 Java 发送到数据库时(比如,插入或更新数据),TypeHandler 确保 Java 类型的数据能够转换成数据库能够理解的格式。这个过程涉及到使用 PreparedStatement,它是一种预编译的 SQL 语句。TypeHandler 负责把 Java 类型的数据正确地放置到 SQL 语句的参数中。

  2. 当你从数据库获取数据时(比如,查询操作),TypeHandler 确保从数据库中获取的数据(通过 ResultSetCallableStatement)能够转换成 Java 程序中能够使用的格式。这样,你就可以在 Java 程序中方便地处理数据库返回的数据。

重要的一点是,MyBatis 已经内置了许多常见基本类型(如整数、字符串等)的类型处理器。这意味着对于这些基本数据类型,MyBatis 能够自动进行 Java 类型和数据库类型之间的转换,你无需做额外工作。

但是,如果你需要处理一些特殊的数据类型(这些类型可能不是基本类型,比如某种特定格式的字符串,或者是你自定义的复杂类型),MyBatis 就无法直接处理了。在这种情况下,你就需要自定义类型处理器。通过自定义类型处理器,你可以指定如何将这些特殊的 Java 类型数据转换为数据库可以理解的类型,反之亦然。

三 自定义 TypeHandler

TypeHandler<T> 接口在 MyBatis 中起着桥梁的作用,它连接了 Java 程序中的数据类型和数据库中的数据类型。这个接口确保了你在 Java 代码中使用的数据类型可以正确地转换成数据库能理解的格式,反之亦然。简单来说,它就像是一个翻译器,帮助 Java 代码和数据库之间进行数据交流。

public interface TypeHandler<T> {/*** 设置 PreparedStatement 的指定参数。* * @param ps        PreparedStatement 对象。* @param index     参数在 PreparedStatement 中的位置。* @param parameter 要设置的参数值。* @param jdbcType  JDBC 类型。这是一个可选参数,可以用来控制设置参数时的行为。* @throws SQLException 如果在设置参数时发生 SQL 异常。*/void setParameter(PreparedStatement ps, int index, T parameter, JdbcType jdbcType) throws SQLException;/*** 从 ResultSet 中获取数据并转换为 Java 类型。* * @param rs        ResultSet 对象。* @param columnName 要获取的数据的列名。* @return 转换后的 Java 类型数据。* @throws SQLException 如果在获取数据时发生 SQL 异常。*/T getResult(ResultSet rs, String columnName) throws SQLException;/*** 从 ResultSet 中获取数据并转换为 Java 类型。* * @param rs         ResultSet 对象。* @param columnIndex 要获取的数据的列索引。* @return 转换后的 Java 类型数据。* @throws SQLException 如果在获取数据时发生 SQL 异常。*/T getResult(ResultSet rs, int columnIndex) throws SQLException;/*** 从 CallableStatement 中获取数据并转换为 Java 类型。* * @param cs         CallableStatement 对象。* @param columnIndex 要获取的数据的列索引。* @return 转换后的 Java 类型数据。* @throws SQLException 如果在获取数据时发生 SQL 异常。*/T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

接口中的方法分别处理不同的数据转换场景:

  1. setParameter(PreparedStatement ps, int index, T parameter, JdbcType jdbcType)

    • 当你在 Java 代码中执行一个 SQL 语句并且需要向这个语句中传入参数时,这个方法就发挥作用了。
    • 它告诉 MyBatis 如何将 Java 类型的数据(parameter)转换成数据库能理解的格式,并把它放在 SQL 语句的正确位置(index)。
  2. getResult(ResultSet rs, String columnName)

    • 当你执行了一个 SQL 查询并从数据库得到结果集(ResultSet)时,这个方法帮助你把结果集中某一列的数据取出,并转换成 Java 类型的数据。
    • 你告诉它具体要转换哪一列(columnName),它就会处理这一列的数据。
  3. getResult(ResultSet rs, int columnIndex)

    • 这个方法和上一个方法类似,但它是通过列的索引(位置)而不是列的名称来获取数据的。
    • 它也是用来把结果集中的数据转换成 Java 类型的数据。
  4. getResult(CallableStatement cs, int columnIndex)

    • 这个方法用在存储过程的场景。存储过程是在数据库中执行的一系列操作,它可以返回多个结果。
    • 当你调用一个存储过程并想要处理返回的结果时,这个方法就会根据你指定的列索引来获取并转换这些结果。

总的来说,TypeHandler<T> 就像是一个双向翻译器,它确保 Java 程序和数据库在数据类型上的沟通是流畅和准确的。

四 创建自定义处理器

实际开发中,我们可以继承 org.apache.ibatis.type.BaseTypeHandler 类型来实现自定义类型处理器。

这个类型是抽象类型,实现了 TypeHandler 的方法进行通用流程的封装,做了异常处理,并定义了几个类似的抽象方法,如下所示。继承 BaseTypeHandler 类型可以极大地降低开发难度。

下面定义三种常用的自定义处理器:

自定义处理器1:数组类型

public class IntegerArrayTypeHandler extends BaseTypeHandler<Integer[]> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Integer[] parameter, JdbcType jdbcType) throws SQLException {// 将Java类型转换为数据库类型Array array = ps.getConnection().createArrayOf("integer", parameter);ps.setArray(i, array);}@Overridepublic Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {// 从数据库类型转换为Java类型Array array = rs.getArray(columnName);return (Integer[]) array.getArray();}@Overridepublic Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {Array array = rs.getArray(columnIndex);return (Integer[]) array.getArray();}@Overridepublic Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {Array array = cs.getArray(columnIndex);return (Integer[]) array.getArray();}
}

自定义处理器2:jsonb类型

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.postgresql.util.PGobject;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class JsonbTypeHandler implements TypeHandler<String> {@Overridepublic void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {PGobject jsonObject = new PGobject();jsonObject.setType("jsonb");jsonObject.setValue(parameter);ps.setObject(i, jsonObject);}@Overridepublic String getResult(ResultSet rs, String columnName) throws SQLException {return rs.getString(columnName);}@Overridepublic String getResult(ResultSet rs, int columnIndex) throws SQLException {return rs.getString(columnIndex);}@Overridepublic String getResult(CallableStatement cs, int columnIndex) throws SQLException {return cs.getString(columnIndex);}
}

自定义处理器3:枚举类型

  1. 定义枚举类
    假设有一个枚举类 StatusEnum,它有两个值:ACTIVE 和 INACTIVE。
public enum StatusEnum {ACTIVE,INACTIVE;public static StatusEnum fromValue(String value) {for (StatusEnum status : values()) {if (status.name().equalsIgnoreCase(value)) {return status;}}throw new IllegalArgumentException("Unknown enum value: " + value);}
}
  1. 创建自定义类型处理器
    创建一个类型处理器来处理 StatusEnum:
    这个类型处理器将数据库中的字符串映射到 StatusEnum 枚举上。它假设数据库中存储的是枚举值的名称(例如,“ACTIVE” 或 “INACTIVE”)。
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class StatusEnumTypeHandler extends BaseTypeHandler<StatusEnum> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, StatusEnum parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, parameter.name());}@Overridepublic StatusEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {String value = rs.getString(columnName);return value == null ? null : StatusEnum.fromValue(value);}@Overridepublic StatusEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String value = rs.getString(columnIndex);return value == null ? null : StatusEnum.fromValue(value);}@Overridepublic StatusEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String value = cs.getString(columnIndex);return value == null ? null : StatusEnum.fromValue(value);}
}

五 把TypeHandler配置到程序中有四种方法:

每一种都测试过,把踩过的坑用黑体标识了。

  1. 在Mapper.xml中声明(应用单个指定字段)
<resultMap id="BaseResultMap" type="com.xxx.EntiyDto"><result column="enum1" jdbcType="INTEGER" property="enum1" typeHandler="com.xxx.handler.IntegerArrayTypeHandler"/>
</resultMap>
  1. 在springboot的yml配置文件中设置类型处理器所在的包名,不是处理器路径(应用到全局
mybatis-plus:type-handlers-package: com.xxx.handler
  1. 实体类指定类型处理器。必须在实体类上加@TableName(autoResultMap = true),否则不生效
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {private Long id;.../*** 注意!! 必须开启映射注解** @TableName(autoResultMap = true)** 以下两种类型处理器,二选一 也可以同时存在** 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包*/@TableField(typeHandler = IntegerArrayTypeHandler.class)private Integer[] integerArray;
}
  1. 在mybatis配置文件中设置
	<typeHandlers><typeHandler handler="com.xxx.handler.IntegerArrayTypeHandler"/></typeHandlers>

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

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

相关文章

Go语言并发模式视角思考

犹记得2019年中旬进行知识点的学习和demo的练习&#xff0c;熟悉各种语法和并发调度的场景&#xff0c; 在2019年末开始参与项目实战开发和逻辑梳理 Go语言的接触也是更多探索和业务的拆件&#xff0c;做一些雏形工具&#xff0c;来慢慢的孵化业务生态 后来陆陆续续&#xff…

时间序列预测 — LSTM实现多变量多步负荷预测(Tensorflow):多输入多输出

目录 1 数据处理 1.1 导入库文件 1.2 导入数据集 ​1.3 缺失值分析 2 构造训练数据 3 LSTM模型训练 4 LSTM模型预测 4.1 分量预测 4.2 可视化 1 数据处理 1.1 导入库文件 import time import datetime import pandas as pd import numpy as np import matplotlib.p…

软件测试|教你如何使用UPDATE修改数据

简介 在SQL&#xff08;Structured Query Language&#xff09;中&#xff0c;UPDATE语句用于修改数据库表中的数据。通过UPDATE语句&#xff0c;我们可以更新表中的特定记录或多条记录&#xff0c;从而实现数据的修改和更新。本文将详细介绍SQL UPDATE语句的语法、用法以及一…

【AI视野·今日Robot 机器人论文速览 第六十七期】Mon, 1 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Mon, 1 Jan 2024 Totally 16 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers MURP: Multi-Agent Ultra-Wideband Relative Pose Estimation with Constrained Communications in 3D Environments Authors A…

【高效视频处理】BMF 项目安装与老视频修复体验全流程及总结

一、BMF简介 BMF&#xff08;Babit Multimedia Framework&#xff09;是字节跳动开发的跨平台、多语言、可定制的多媒体处理框架。经过 4 年多的测试和改进&#xff0c;BMF 已经过量身定制&#xff0c;能够熟练地应对我们现实生产环境中的挑战。目前广泛应用于字节跳动的视频串…

JS题库复习(JavaScript标记版)

JavaScript标记版 1 "有以下 ES6 代码 function * gen() { yield 1; yield 2; yield 3; } 下面选项描述正确的是哪个&#xff1f;" A gen()执行后返回2 B gen()执行后返回 undefined C gen()执行后返回一个 Generator 对象 D gen()执行后返回 1 C Ja…

主流大语言模型从预训练到微调的技术原理

引言 本文设计的内容主要包含以下几个方面&#xff1a; 比较 LLaMA、ChatGLM、Falcon 等大语言模型的细节&#xff1a;tokenizer、位置编码、Layer Normalization、激活函数等。大语言模型的分布式训练技术&#xff1a;数据并行、张量模型并行、流水线并行、3D 并行、零冗余优…

机器学习--回归算法

&#x1f333;&#x1f333;&#x1f333;小谈&#xff1a;一直想整理机器学习的相关笔记&#xff0c;但是一直在推脱&#xff0c;今天发现知识快忘却了&#xff08;虽然学的也不是那么深&#xff09;&#xff0c;但还是浅浅整理一下吧&#xff0c;便于以后重新学习。 &#x1…

IOS:Safari无法播放MP4(H.264编码)

一、问题描述 MP4使用H.264编码通常具有良好的兼容性&#xff0c;因为H.264是一种广泛支持的视频编码标准。它可以在许多设备和平台上播放&#xff0c;包括电脑、移动设备和流媒体设备。 使用caniuse查询H.264兼容性&#xff0c;看似确实具有良好的兼容性&#xff1a; 然而…

树莓派和电脑之间的串口通信

一开始我们配置了串口登录树莓派&#xff0c;这样会导致我们后面在用树莓派的时候有些冲突&#xff0c;所以我们需要先修改树莓派串口的一些配置 /* 修改 cmdline.txt文件 */ >cd /boot/ >sudo vim cmdline.txt 删除【】之间的部分 dwc_otg.lpm_enable0 【consolettyAMA0…

【响应式编程-05】Lambda方法引用

一、简要描述 Lambda的方法引用也叫引用方法 方法引用初体验方法引用的底层实现方法引用的语法格式方法引用举例 静态方法引用构造方法引用普通方法引用super和this方法引用数组的方法引用 二、方法引用初体验 为什么出现方法引用&#xff1f; 引用已存在方法&#xff0c;避免重…

四则运算 C语言xdoj20

问题描述&#xff1a; 输入两个整数和一个四则运算符&#xff0c;根据运算符计算并输出其运算结果&#xff08;和、差、积、商、余之一&#xff09;。注意做整除及求余运算时&#xff0c;除数不能为零。 输入说明&#xff1a; 使用scanf()函数输入两个整数和一个运算符&#xf…

政府采购变数大,AI PC是联想的“新希望”?

文&#xff5c;新熔财经 作者&#xff5c;余一 发布两款AI PC&#xff0c;并预热CES将有AI PC大动作后&#xff0c;联想似乎找到了计算机终端的新思路。 而在这之前&#xff0c;联想终端业务面临的挑战不可谓不严重。 “事业单位更换纯国产电脑”、“联想被排除在大订单之外…

前端面试题-nodejs

1.什么是nodejs&#xff0c;它与传统的网页服务器有什么不同&#xff1f; 是什么&#xff1f;nodejs是基于Chrome V8引擎的JavaScript运行环境&#xff0c;它可以使JavaScript代码在服务器上运行。 有什么不同&#xff1f;第一&#xff0c;nodejs采用事件驱动、非阻塞式I/O模型…

汽车电子行业的 C 语言编程标准

前言 之前分享了一些编程规范相关的文章&#xff0c;有位读者提到了汽车电子行业的MISRA C标准&#xff0c;说这个很不错。 本次给大家找来了一篇汽车电子行业的MISRA C标准的文章一同学习下。 什么是MISRA&#xff1f; MISRA (The Motor Industry Software Reliability Ass…

微型导轨在设备中起什么作用

微型导轨精度高&#xff0c;摩擦系数小&#xff0c;自重轻&#xff0c;结构紧凑&#xff0c;可以用于电子制造设备、半导体制造设备、医疗设备、光学设备和机器人等各种工业机械设备中&#xff0c;那么微型导轨在设备中起什么作用呢&#xff1f; 1、导向与定位&#xff1a;为机…

Spring 应用合并之路(二):峰回路转,柳暗花明 | 京东云技术团队

书接上文&#xff0c;前面在 [Spring 应用合并之路&#xff08;一&#xff09;&#xff1a;摸石头过河]介绍了几种不成功的经验&#xff0c;下面继续折腾… 四、仓库合并&#xff0c;独立容器 在经历了上面的尝试&#xff0c;在同事为啥不搞两个独立的容器提醒下&#xff0c;…

Web网页开发-CSS定位-笔记

一、CSS的三种布局 (1) 标准流 块级元素:一行一个 行内元素:一行多个&#xff0c;margin上下无效 行内块元素:一行多个 (2) 浮动 让元素同处一行 (3) 定位 让元素在浏览器中任何位置,并且覆盖住其他元素&#xff0c;能够固定死在浏览器上的某个位置&#xff0c;不随滚动条滚动 …

C++面对对象编程进阶(1)

面对对象编程进阶&#xff08;1&#xff09; 1.初始化列表2.类的继承3.深挖公有、私有及保护4.友元类5.类指针 1.初始化列表 C中类的初始化列表应用于构造函数初始化类的成员变量。其具体应用方法为&#xff1a; class Student {public:int age;string name;Student():age(0)…

G4周:CGAN,手势生成

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 我的环境&#xff1a; 1.语言&#xff1a;python3.7 2.编译器&#xff1a;pycharm 3.深度学习框架Pytorch 1.8.0cu111 一、CGAN介绍 条件生成对抗网络&#xff08;…