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

本文主要是介绍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>

这篇关于MyBatis自定义类型处理器 TypeHandler(通俗易懂,效率起飞),处理jsonb、数组、枚举类型。支持MybatisPlus的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/574337

相关文章

Python自动化处理手机验证码

《Python自动化处理手机验证码》手机验证码是一种常见的身份验证手段,广泛应用于用户注册、登录、交易确认等场景,下面我们来看看如何使用Python自动化处理手机验证码吧... 目录一、获取手机验证码1.1 通过短信接收验证码1.2 使用第三方短信接收服务1.3 使用ADB读取手机短信1.4 通过API获取

Python自动化Office文档处理全攻略

《Python自动化Office文档处理全攻略》在日常办公中,处理Word、Excel和PDF等Office文档是再常见不过的任务,手动操作这些文档不仅耗时耗力,还容易出错,幸运的是,Python提供... 目录一、自动化处理Word文档1. 安装python-docx库2. 读取Word文档内容3. 修改

Rust中的BoxT之堆上的数据与递归类型详解

《Rust中的BoxT之堆上的数据与递归类型详解》本文介绍了Rust中的BoxT类型,包括其在堆与栈之间的内存分配,性能优势,以及如何利用BoxT来实现递归类型和处理大小未知类型,通过BoxT,Rus... 目录1. Box<T> 的基础知识1.1 堆与栈的分工1.2 性能优势2.1 递归类型的问题2.2

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

定价129元!支持双频 Wi-Fi 5的华为AX1路由器发布

《定价129元!支持双频Wi-Fi5的华为AX1路由器发布》华为上周推出了其最新的入门级Wi-Fi5路由器——华为路由AX1,建议零售价129元,这款路由器配置如何?详细请看下文介... 华为 Wi-Fi 5 路由 AX1 已正式开售,新品支持双频 1200 兆、配有四个千兆网口、提供可视化智能诊断功能,建

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相