TypeHandler是MyBatis中用于处理Java类型与JDBC类型之间转换的接口。在SQL语句执行过程中,无论是设置参数还是获取结果集,都需要通过TypeHandler进行类型转换。MyBatis提供了丰富的内置TypeHandler实现,以支持常见的数据类型转换。同时,也可以根据需要自定义TypeHandler来处理特殊的数据类型或转换逻辑。
TypeHandler在MyBatis中是一个核心概念,其工作原理主要涉及Java类型和JDBC类型之间的转换。下面将详细介绍TypeHandler的工作原理。
当MyBatis执行一个预编译的SQL语句(如INSERT、UPDATE等)时,它需要将Java对象中的属性值设置到SQL语句中对应的占位符上。这个过程就是通过TypeHandler来实现的。
具体步骤如下:
当数据库执行查询操作并返回结果集时,MyBatis需要将结果集中的数据提取出来,并转换为Java对象中的对应属性类型。这个过程同样是通过TypeHandler来实现的。
具体步骤如下:
TypeHandler的核心功能是实现Java类型和JDBC类型之间的映射和转换。这个映射和转换规则是根据Java类型和JDBC类型的特性和语义来定义的。
MyBatis的TypeHandler机制具有很高的扩展性。开发者可以通过实现TypeHandler接口或继承BaseTypeHandler类来创建自定义的TypeHandler实现。自定义的TypeHandler可以实现任意复杂的类型转换逻辑,以满足特定业务需求。
此外,MyBatis还提供了丰富的API和扩展点来支持开发者自定义TypeHandler的注册和使用方式。开发者可以通过配置文件、注解或编程方式将自定义的TypeHandler注册到MyBatis中,并在Mapper的XML映射文件中引用它们来处理特定的数据类型转换需求。
MyBatis为了简化开发者的工作,提供了一系列内置的TypeHandler,这些内置的TypeHandler能够处理大部分常见的数据类型转换。以下是一些MyBatis中常见的内置TypeHandler:
Boolean
类型与数据库中的BOOLEAN
、BIT
等类型的映射关系。Integer
、Long
、Short
等整数类型与数据库中的相应整数类型的映射,如INT
、BIGINT
、SMALLINT
等。Float
、Double
和BigDecimal
,与数据库中的FLOAT
、DOUBLE
、DECIMAL
等类型的映射。String
类型与数据库中的字符类型如VARCHAR
、CHAR
、TEXT
等的映射关系。Date
、Time
、Timestamp
,与数据库中的DATE
、TIME
、TIMESTAMP
等类型的映射。byte[]
类型与数据库中的二进制类型的映射,如BLOB
、BINARY
等。Clob
(字符大对象)和Blob
(二进制大对象)类型与数据库中的CLOB
和BLOB
类型的映射。EnumTypeHandler
将枚举名称存储到数据库,而EnumOrdinalTypeHandler
将枚举的序数(ordinal)存储到数据库。java.sql.Types
中的常量)来确定具体的类型处理方式。以上只是MyBatis内置TypeHandler的一部分示例,实际上MyBatis提供了更多的内置TypeHandler以支持各种不同类型的数据转换需求。在使用MyBatis时,可以根据具体的数据库类型和Java类型选择合适的内置TypeHandler,或者根据需要自定义TypeHandler来处理特殊的数据类型转换场景。
在某些情况下,我们可能需要处理一些特殊的数据类型或者实现复杂的类型转换逻辑。这时,就需要自定义TypeHandler。下面是在Spring Boot环境中自定义TypeHandler的步骤:
自定义TypeHandler需要实现org.apache.ibatis.type.TypeHandler接口,或者继承org.apache.ibatis.type.BaseTypeHandler类,并重写相应的方法。
org.apache.ibatis.type.TypeHandler
TypeHandler是一个接口,用于定义如何处理JDBC类型和Java类型之间的转换。当你需要创建一个自定义的类型处理器时,通常需要实现这个接口。这个接口定义了以下主要方法:
setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
:用于设置PreparedStatement对象的指定参数。getResult(ResultSet rs, String columnName)
:从结果集中根据列名获取数据。getResult(ResultSet rs, int columnIndex)
:从结果集中根据列索引获取数据。getResult(CallableStatement cs, int columnIndex)
:从存储过程的结果集中根据列索引获取数据。这些方法分别负责在SQL语句执行时将Java类型的参数转换成JDBC类型,以及在执行SQL查询后将JDBC类型的结果转换成Java类型。
org.apache.ibatis.type.BaseTypeHandler
BaseTypeHandler是TypeHandler
接口的一个抽象实现类,它提供了一些基本的实现,使得创建自定义类型处理器时只需要覆盖必要的方法。通常,如果你不需要处理所有的JDBC类型,可以选择继承BaseTypeHandler
以减少工作量。
BaseTypeHandler
提供了对null
值的处理以及部分TypeHandler
接口方法的默认实现。当你继承BaseTypeHandler
时,通常需要实现或覆盖以下方法:
setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
:设置非空的参数值。getNullableResult(ResultSet rs, String columnName)
:根据列名获取可能为null的结果。getNullableResult(ResultSet rs, int columnIndex)
:根据列索引获取可能为null的结果。getNullableResult(CallableStatement cs, int columnIndex)
:从存储过程的结果集中根据列索引获取可能为null的结果。这些方法专注于处理非空值的转换以及处理从数据库中检索的可能为null的值。
总的来说,TypeHandler
接口提供了完整的JDBC类型和Java类型转换的契约,而BaseTypeHandler
则是一个便利的基类,提供了一些基本的和通用的实现,以减少自定义类型处理器时的代码量。在创建自定义类型处理器时,可以根据具体需求选择直接实现TypeHandler
接口还是继承BaseTypeHandler
类。
在MyBatis的配置文件中注册自定义的TypeHandler。如果是在Spring Boot环境中使用MyBatis,可以通过在application.properties
或application.yml
文件中配置mybatis.type-handlers-package
属性来指定TypeHandler所在的包路径,MyBatis会自动扫描并注册该包下的所有TypeHandler。
在Mapper的XML映射文件中,通过resultType
或parameterType
属性引用自定义的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> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
ps.setTimestamp(i, java.sql.Timestamp.valueOf(parameter));
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
java.sql.Timestamp timestamp = rs.getTimestamp(columnName);
return timestamp != null ? timestamp.toLocalDateTime() : null;
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
java.sql.Timestamp timestamp = rs.getTimestamp(columnIndex);
return timestamp != null ? timestamp.toLocalDateTime() : null;
}
@Override
public 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 configuration
PUBLIC "-//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.properties
或application.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,你通常有两种方法:
自定义TypeHandler的应用场景非常广泛。以下是一些常见的应用场景:
MyBatis的TypeHandler机制为Java类型和JDBC类型之间的转换提供了灵活且强大的支持。通过内置和自定义的TypeHandler,我们可以轻松处理各种数据类型转换需求,提高开发效率和代码可维护性。在Spring Boot环境中使用自定义TypeHandler更是简化了配置和注册过程,使得开发者能够更专注于业务逻辑的实现。
术因分享而日新,每获新知,喜溢心扉。 诚邀关注公众号 『
码到三十五
』 ,获取更多技术资料。