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

相关文章

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

idea的终端(Terminal)cmd的命令换成linux的命令详解

《idea的终端(Terminal)cmd的命令换成linux的命令详解》本文介绍IDEA配置Git的步骤:安装Git、修改终端设置并重启IDEA,强调顺序,作为个人经验分享,希望提供参考并支持脚本之... 目录一编程、设置前二、前置条件三、android设置四、设置后总结一、php设置前二、前置条件

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3