MyBatis TypeHandler详解:原理与自定义实践

2024-03-03 18:36

本文主要是介绍MyBatis TypeHandler详解:原理与自定义实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在MyBatis中,TypeHandler扮演着一个至关重要的角色,它负责Java类型和JDBC类型之间的映射和转换。本文将详细介绍MyBatis TypeHandler的概念、工作原理,以及如何在Spring Boot环境中自定义TypeHandler,并通过案例来展示其应用场景。

目录

    • 一、TypeHandler简介
    • 二、TypeHandler的工作原理
      • 1. 设置参数(Parameter Setting)
      • 2. 获取结果(Result Getting)
      • 3. 类型映射和转换规则
      • 4. 自定义TypeHandler的扩展性
    • 三、内置TypeHandler介绍
    • 四、自定义TypeHandler实践
      • 1. 创建自定义TypeHandler类
        • `org.apache.ibatis.type.TypeHandler`
        • `org.apache.ibatis.type.BaseTypeHandler`
      • 2. 注册自定义TypeHandler
      • 3. 在Mapper中使用自定义TypeHandler
      • 自定义时间转换案例
    • 五、应用场景
    • 六、总结

一、TypeHandler简介

TypeHandler是MyBatis中用于处理Java类型与JDBC类型之间转换的接口。在SQL语句执行过程中,无论是设置参数还是获取结果集,都需要通过TypeHandler进行类型转换。MyBatis提供了丰富的内置TypeHandler实现,以支持常见的数据类型转换。同时,也可以根据需要自定义TypeHandler来处理特殊的数据类型或转换逻辑。

二、TypeHandler的工作原理

TypeHandler在MyBatis中是一个核心概念,其工作原理主要涉及Java类型和JDBC类型之间的转换。下面将详细介绍TypeHandler的工作原理。

1. 设置参数(Parameter Setting)

当MyBatis执行一个预编译的SQL语句(如INSERT、UPDATE等)时,它需要将Java对象中的属性值设置到SQL语句中对应的占位符上。这个过程就是通过TypeHandler来实现的。

具体步骤如下:

  • MyBatis会根据映射配置找到对应的TypeHandler实例。这个映射配置可以在MyBatis的配置文件或者Mapper的XML文件中定义。
  • TypeHandler实例会接收到Java对象中的属性值,并将其转换为JDBC能够识别的类型。这个转换过程是根据Java类型和JDBC类型之间的映射关系来实现的。
  • 转换后的值会被设置到PreparedStatement对象中对应的占位符上,以便数据库能够正确解析和执行SQL语句。

2. 获取结果(Result Getting)

当数据库执行查询操作并返回结果集时,MyBatis需要将结果集中的数据提取出来,并转换为Java对象中的对应属性类型。这个过程同样是通过TypeHandler来实现的。

具体步骤如下:

  • MyBatis会根据映射配置找到对应的TypeHandler实例。
  • TypeHandler实例会从ResultSet对象中提取数据,这个提取过程是根据数据库字段和Java属性之间的映射关系来实现的。
  • 提取出的数据会被转换为Java对象中的对应属性类型。这个转换过程同样是根据Java类型和JDBC类型之间的映射关系来实现的。
  • 转换后的值会被设置到Java对象中对应的属性上,以便应用程序能够正确处理和使用这些数据。

3. 类型映射和转换规则

TypeHandler的核心功能是实现Java类型和JDBC类型之间的映射和转换。这个映射和转换规则是根据Java类型和JDBC类型的特性和语义来定义的。

  • 对于基本数据类型(如int、long、float等),MyBatis提供了内置的TypeHandler实现,这些实现能够直接将Java基本数据类型转换为对应的JDBC基本数据类型,反之亦然。
  • 对于复杂数据类型(如自定义对象、集合等),MyBatis允许开发者自定义TypeHandler来实现复杂的类型转换逻辑。例如,开发者可以定义一个自定义的TypeHandler来将数据库中的JSON字符串转换为Java中的对象,或者将Java对象转换为JSON字符串存储到数据库中。

4. 自定义TypeHandler的扩展性

MyBatis的TypeHandler机制具有很高的扩展性。开发者可以通过实现TypeHandler接口或继承BaseTypeHandler类来创建自定义的TypeHandler实现。自定义的TypeHandler可以实现任意复杂的类型转换逻辑,以满足特定业务需求。

此外,MyBatis还提供了丰富的API和扩展点来支持开发者自定义TypeHandler的注册和使用方式。开发者可以通过配置文件、注解或编程方式将自定义的TypeHandler注册到MyBatis中,并在Mapper的XML映射文件中引用它们来处理特定的数据类型转换需求。

三、内置TypeHandler介绍

MyBatis为了简化开发者的工作,提供了一系列内置的TypeHandler,这些内置的TypeHandler能够处理大部分常见的数据类型转换。以下是一些MyBatis中常见的内置TypeHandler:

  1. BooleanTypeHandler
    处理Java的Boolean类型与数据库中的BOOLEANBIT等类型的映射关系。

  2. IntegerTypeHandlerLongTypeHandlerShortTypeHandler 等:
    这些TypeHandler分别处理Java中的IntegerLongShort等整数类型与数据库中的相应整数类型的映射,如INTBIGINTSMALLINT等。

  3. FloatTypeHandlerDoubleTypeHandlerBigDecimalTypeHandler
    处理Java中的浮点数和定点数类型,如FloatDoubleBigDecimal,与数据库中的FLOATDOUBLEDECIMAL等类型的映射。

  4. StringTypeHandler
    处理Java的String类型与数据库中的字符类型如VARCHARCHARTEXT等的映射关系。

  5. DateTypeHandlerTimeTypeHandlerTimestampTypeHandler
    这些TypeHandler处理Java中的日期和时间类型,如DateTimeTimestamp,与数据库中的DATETIMETIMESTAMP等类型的映射。

  6. ByteArrayTypeHandler
    处理Java的byte[]类型与数据库中的二进制类型的映射,如BLOBBINARY等。

  7. ClobTypeHandlerBlobTypeHandler
    分别处理Java中的Clob(字符大对象)和Blob(二进制大对象)类型与数据库中的CLOBBLOB类型的映射。

  8. EnumTypeHandlerEnumOrdinalTypeHandler
    这两个TypeHandler用于处理Java中的枚举类型。EnumTypeHandler将枚举名称存储到数据库,而EnumOrdinalTypeHandler将枚举的序数(ordinal)存储到数据库。

  9. JdbcTypeHandler
    这是一个通用的TypeHandler,它根据JDBC类型(java.sql.Types中的常量)来确定具体的类型处理方式。

  10. UnknownTypeHandler
    当MyBatis无法确定具体的类型处理方式时,会使用这个TypeHandler。通常,这是一个最后的备选方案,它会尝试将值作为对象(Object)来处理。

以上只是MyBatis内置TypeHandler的一部分示例,实际上MyBatis提供了更多的内置TypeHandler以支持各种不同类型的数据转换需求。在使用MyBatis时,可以根据具体的数据库类型和Java类型选择合适的内置TypeHandler,或者根据需要自定义TypeHandler来处理特殊的数据类型转换场景。

四、自定义TypeHandler实践

在某些情况下,我们可能需要处理一些特殊的数据类型或者实现复杂的类型转换逻辑。这时,就需要自定义TypeHandler。下面是在Spring Boot环境中自定义TypeHandler的步骤:

1. 创建自定义TypeHandler类

自定义TypeHandler需要实现org.apache.ibatis.type.TypeHandler接口,或者继承org.apache.ibatis.type.BaseTypeHandler类,并重写相应的方法。

org.apache.ibatis.type.TypeHandler

TypeHandler是一个接口,用于定义如何处理JDBC类型和Java类型之间的转换。当你需要创建一个自定义的类型处理器时,通常需要实现这个接口。这个接口定义了以下主要方法:

  1. setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType):用于设置PreparedStatement对象的指定参数。
  2. getResult(ResultSet rs, String columnName):从结果集中根据列名获取数据。
  3. getResult(ResultSet rs, int columnIndex):从结果集中根据列索引获取数据。
  4. getResult(CallableStatement cs, int columnIndex):从存储过程的结果集中根据列索引获取数据。

这些方法分别负责在SQL语句执行时将Java类型的参数转换成JDBC类型,以及在执行SQL查询后将JDBC类型的结果转换成Java类型。

org.apache.ibatis.type.BaseTypeHandler

BaseTypeHandler是TypeHandler接口的一个抽象实现类,它提供了一些基本的实现,使得创建自定义类型处理器时只需要覆盖必要的方法。通常,如果你不需要处理所有的JDBC类型,可以选择继承BaseTypeHandler以减少工作量。

BaseTypeHandler 提供了对null值的处理以及部分TypeHandler接口方法的默认实现。当你继承BaseTypeHandler时,通常需要实现或覆盖以下方法:

  1. setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType):设置非空的参数值。
  2. getNullableResult(ResultSet rs, String columnName):根据列名获取可能为null的结果。
  3. getNullableResult(ResultSet rs, int columnIndex):根据列索引获取可能为null的结果。
  4. getNullableResult(CallableStatement cs, int columnIndex):从存储过程的结果集中根据列索引获取可能为null的结果。

这些方法专注于处理非空值的转换以及处理从数据库中检索的可能为null的值。

总的来说,TypeHandler接口提供了完整的JDBC类型和Java类型转换的契约,而BaseTypeHandler则是一个便利的基类,提供了一些基本的和通用的实现,以减少自定义类型处理器时的代码量。在创建自定义类型处理器时,可以根据具体需求选择直接实现TypeHandler接口还是继承BaseTypeHandler类。

2. 注册自定义TypeHandler

在MyBatis的配置文件中注册自定义的TypeHandler。如果是在Spring Boot环境中使用MyBatis,可以通过在application.propertiesapplication.yml文件中配置mybatis.type-handlers-package属性来指定TypeHandler所在的包路径,MyBatis会自动扫描并注册该包下的所有TypeHandler。

3. 在Mapper中使用自定义TypeHandler

在Mapper的XML映射文件中,通过resultTypeparameterType属性引用自定义的TypeHandler。例如,在<select>标签中设置resultType="com.example.CustomType"来指定使用自定义的TypeHandler处理查询结果。

自定义时间转换案例

首先,创建一个自定义的TypeHandler来处理LocalDateTime类型与数据库中的时间戳类型之间的转换。

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;@MappedJdbcTypes(JdbcType.TIMESTAMP) // 指定对应的JDBC类型
@MappedTypes(LocalDateTime.class) // 指定对应的Java类型
public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {ps.setTimestamp(i, java.sql.Timestamp.valueOf(parameter));}@Overridepublic LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {java.sql.Timestamp timestamp = rs.getTimestamp(columnName);return timestamp != null ? timestamp.toLocalDateTime() : null;}@Overridepublic LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {java.sql.Timestamp timestamp = rs.getTimestamp(columnIndex);return timestamp != null ? timestamp.toLocalDateTime() : null;}@Overridepublic LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {java.sql.Timestamp timestamp = cs.getTimestamp(columnIndex);return timestamp != null ? timestamp.toLocalDateTime() : null;}
}

然后,在Spring Boot的配置中,你需要确保MyBatis能够扫描到这个TypeHandler。通常,MyBatis会自动扫描与Mapper接口相同的包路径下的TypeHandler。但是,如果你想要更明确地指定TypeHandler的位置,你可以通过MyBatis的配置文件来做到这一点。

src/main/resources目录下创建MyBatis的配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeHandlers><package name="com.example.demo.typehandler"/> <!-- 指定TypeHandler的包名 --></typeHandlers>
</configuration>

application.propertiesapplication.yml中指定MyBatis配置文件的位置:

# application.properties
mybatis.config-location=classpath:mybatis-config.xml

确保你的Mapper接口使用了LocalDateTime类型的字段,并且你的数据库表中有对应的时间戳字段。

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.time.LocalDateTime;
import java.util.List;@Mapper
public interface ExampleMapper {@Select("SELECT * FROM example_table")List<ExampleEntity> selectAll();// 其他CRUD操作...
}// 对应的实体类
public class ExampleEntity {private Long id;private LocalDateTime createdAt;// 省略getter和setter方法...
}

最后,在Spring Boot的启动类或配置类上使用@MapperScan注解来指定Mapper接口所在的包名:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 指定Mapper接口所在的包名
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

确保你的Spring Boot项目中包含了MyBatis和MyBatis Spring Boot Starter的依赖项。你可以在pom.xml中添加如下依赖:

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version> <!-- 使用适合你项目的版本 -->
</dependency>

现在,当你运行Spring Boot应用时,MyBatis将会使用你自定义的LocalDateTimeTypeHandler来处理LocalDateTime类型与数据库中的TIMESTAMP类型之间的转换。注意,由于我们使用了@MappedJdbcTypes@MappedTypes注解,MyBatis将自动识别并使用这个TypeHandler。如果你没有在TypeHandler上使用这些注解,你需要在MyBatis配置文件中显式注册TypeHandler

要在MyBatis中注册自定义的TypeHandler,你通常有两种方法:

  1. 使用注解:直接在TypeHandler类上使用@MappedJdbcTypes和@MappedTypes注解。这种方法在你已经使用的示例中展示了。当MyBatis扫描到带有这些注解的TypeHandler时,它会自动注册。
  2. 在MyBatis配置文件中注册:如果你没有使用注解,或者想要更明确地注册TypeHandler,你可以在MyBatis的配置文件中手动添加它。

五、应用场景

自定义TypeHandler的应用场景非常广泛。以下是一些常见的应用场景:

  1. 处理枚举类型:将数据库中的整数值映射为Java中的枚举类型,或者将枚举类型转换为数据库中的整数值。

  2. 处理复杂数据类型:如将数据库中的JSON字符串映射为Java中的对象,或者将Java对象转换为JSON字符串存储到数据库中。

  3. 实现特殊的类型转换逻辑:如将数据库中的日期字符串转换为Java中的特定日期对象格式。

  4. 兼容不同的数据库类型:当使用不同类型的数据库时,可能需要处理不同类型之间的转换差异。通过自定义TypeHandler可以实现数据库类型之间的兼容转换。

六、总结

MyBatis的TypeHandler机制为Java类型和JDBC类型之间的转换提供了灵活且强大的支持。通过内置和自定义的TypeHandler,我们可以轻松处理各种数据类型转换需求,提高开发效率和代码可维护性。在Spring Boot环境中使用自定义TypeHandler更是简化了配置和注册过程,使得开发者能够更专注于业务逻辑的实现。

这篇关于MyBatis TypeHandler详解:原理与自定义实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

pycharm远程连接服务器运行pytorch的过程详解

《pycharm远程连接服务器运行pytorch的过程详解》:本文主要介绍在Linux环境下使用Anaconda管理不同版本的Python环境,并通过PyCharm远程连接服务器来运行PyTorc... 目录linux部署pytorch背景介绍Anaconda安装Linux安装pytorch虚拟环境安装cu

一文详解如何在Python中使用Requests库

《一文详解如何在Python中使用Requests库》:本文主要介绍如何在Python中使用Requests库的相关资料,Requests库是Python中常用的第三方库,用于简化HTTP请求的发... 目录前言1. 安装Requests库2. 发起GET请求3. 发送带有查询参数的GET请求4. 发起PO

Python进行PDF文件拆分的示例详解

《Python进行PDF文件拆分的示例详解》在日常生活中,我们常常会遇到大型的PDF文件,难以发送,将PDF拆分成多个小文件是一个实用的解决方案,下面我们就来看看如何使用Python实现PDF文件拆分... 目录使用工具将PDF按页数拆分将PDF的每一页拆分为单独的文件将PDF按指定页数拆分根据页码范围拆分

Java中的Cursor使用详解

《Java中的Cursor使用详解》本文介绍了Java中的Cursor接口及其在大数据集处理中的优势,包括逐行读取、分页处理、流控制、动态改变查询、并发控制和减少网络流量等,感兴趣的朋友一起看看吧... 最近看代码,有一段代码涉及到Cursor,感觉写法挺有意思的。注意是Cursor,而不是Consumer

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

HTML5中下拉框<select>标签的属性和样式详解

《HTML5中下拉框<select>标签的属性和样式详解》在HTML5中,下拉框(select标签)作为表单的重要组成部分,为用户提供了一个从预定义选项中选择值的方式,本文将深入探讨select标签的... 在html5中,下拉框(<select>标签)作为表单的重要组成部分,为用户提供了一个从预定义选项中

Spring Boot 3 整合 Spring Cloud Gateway实践过程

《SpringBoot3整合SpringCloudGateway实践过程》本文介绍了如何使用SpringCloudAlibaba2023.0.0.0版本构建一个微服务网关,包括统一路由、限... 目录引子为什么需要微服务网关实践1.统一路由2.限流防刷3.登录鉴权小结引子当前微服务架构已成为中大型系统的标

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

Java 8 Stream filter流式过滤器详解

《Java8Streamfilter流式过滤器详解》本文介绍了Java8的StreamAPI中的filter方法,展示了如何使用lambda表达式根据条件过滤流式数据,通过实际代码示例,展示了f... 目录引言 一.Java 8 Stream 的过滤器(filter)二.Java 8 的 filter、fi