5. MyBatis 如何实现数据库类型和 Java 类型的转换的?
MyBatis 在处理数据库查询结果或传递参数时,需要将数据库类型与 Java 类型之间进行转换。MyBatis 提供了多种方式来实现这种类型转换,主要通过内置的 TypeHandler(类型处理器)机制。
1. TypeHandler 的作用
TypeHandler
是 MyBatis 中的一个接口,用于在 Java 类型和 JDBC 类型之间进行转换。每当 MyBatis 需要将 Java 对象传递给 SQL 语句,或者从 SQL 查询结果中获取数据并转换为 Java 对象时,都会使用 TypeHandler
。
TypeHandler
负责以下两种类型的转换:
- Java 类型到 JDBC 类型的转换:当 MyBatis 需要将 Java 对象作为参数传递给 SQL 语句时,
TypeHandler
会将 Java 类型转换为 JDBC 类型。 - JDBC 类型到 Java 类型的转换:当 MyBatis 从数据库中获取数据时,
TypeHandler
会将 JDBC 类型转换为 Java 类型。
2. 内置的 TypeHandler
MyBatis 提供了许多内置的 TypeHandler
,可以处理常见的 Java 类型和 JDBC 类型之间的转换。以下是一些常见的内置 TypeHandler
:
IntegerTypeHandler
:用于将int
和Integer
转换为数据库中的INTEGER
类型。StringTypeHandler
:用于将String
转换为数据库中的VARCHAR
类型。DateTypeHandler
:用于将java.util.Date
转换为数据库中的DATE
类型。BooleanTypeHandler
:用于将boolean
和Boolean
转换为数据库中的BIT
或BOOLEAN
类型。
这些内置的 TypeHandler
在绝大多数情况下能够满足开发者的需求。MyBatis 会自动选择合适的 TypeHandler
来处理常见的数据类型。
3. 自定义 TypeHandler
有时,应用程序中可能需要处理一些自定义的数据类型,或者需要对现有的类型转换进行定制。此时可以通过实现 TypeHandler
接口来自定义类型处理器。
3.1. 实现 TypeHandler
接口
要创建一个自定义的 TypeHandler
,你需要实现 org.apache.ibatis.type.TypeHandler
接口。这个接口有四个方法:
setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
:将 Java 类型的数据设置到PreparedStatement
中,作为 SQL 语句的参数。getResult(ResultSet rs, String columnName)
:从ResultSet
中通过列名获取数据,并将其转换为 Java 类型。getResult(ResultSet rs, int columnIndex)
:从ResultSet
中通过列索引获取数据,并将其转换为 Java 类型。getResult(CallableStatement cs, int columnIndex)
:从CallableStatement
中获取数据,并将其转换为 Java 类型(用于处理存储过程的输出参数)。
3.2. 自定义 TypeHandler 示例
假设你有一个枚举类 Gender
,它表示用户的性别。数据库中性别使用 CHAR
类型存储,值为 'M' 或 'F',而在 Java 中使用枚举来表示:
public enum Gender {
MALE, FEMALE
}
你可以创建一个自定义的 TypeHandler
,用于将 CHAR
类型的数据库值转换为 Gender
枚举,并反向转换:
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
public class GenderTypeHandler extends BaseTypeHandler<Gender> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Gender gender, JdbcType jdbcType) throws SQLException {
ps.setString(i, gender == Gender.MALE ? "M" : "F");
}
@Override
public Gender getNullableResult(ResultSet rs, String columnName) throws SQLException {
String gender = rs.getString(columnName);
return gender != null && gender.equals("M") ? Gender.MALE : Gender.FEMALE;
}
@Override
public Gender getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String gender = rs.getString(columnIndex);
return gender != null && gender.equals("M") ? Gender.MALE : Gender.FEMALE;
}
@Override
public Gender getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String gender = cs.getString(columnIndex);
return gender != null && gender.equals("M") ? Gender.MALE : Gender.FEMALE;
}
}
3.3. 注册自定义 TypeHandler
你可以通过两种方式将自定义的 TypeHandler
注册到 MyBatis:
在 MyBatis 配置文件中注册:
<typeHandlers> <typeHandler handler="com.example.GenderTypeHandler" javaType="com.example.Gender"/> </typeHandlers>
通过注解注册:
如果你使用 MyBatis 的 Mapper 注解,可以在字段或方法上直接使用
@TypeHandler
注解:@Select("SELECT id, name, gender FROM users WHERE id = #{id}") @Results({ @Result(property = "gender", column = "gender", typeHandler = GenderTypeHandler.class) }) User selectUserById(int id);
4. 全局和局部 TypeHandler
- 全局 TypeHandler:可以通过在 MyBatis 配置文件中注册自定义
TypeHandler
,或者使用@MappedTypes
注解,将自定义TypeHandler
绑定到特定的 Java 类型。MyBatis 会自动在需要时使用这些全局TypeHandler
。 - 局部 TypeHandler:在特定的 SQL 映射文件或注解中指定
TypeHandler
。这种方式仅在指定的地方生效,不会影响全局行为。
5. 总结
- 内置
TypeHandler
:MyBatis 提供了多种内置的TypeHandler
,用于处理常见的 Java 类型和数据库类型之间的转换。 - 自定义
TypeHandler
:当内置的TypeHandler
不能满足需求时,可以通过实现TypeHandler
接口或扩展BaseTypeHandler
类来自定义类型处理器,并在 MyBatis 中注册使用。 - 注册与使用:自定义的
TypeHandler
可以全局注册,也可以在特定的映射文件或注解中局部使用,灵活处理复杂的类型转换需求。
通过使用 TypeHandler
,MyBatis 能够灵活且高效地处理 Java 类型和数据库类型之间的转换,满足各种应用场景的需求。