17. 什么是MyBatis中的`TypeHandler`?如何自定义`TypeHandler`处理复杂类型?
TypeHandler
是 MyBatis 中的一个接口,用于在 Java 类型和 JDBC 类型之间进行转换。每当 MyBatis 需要将 Java 对象传递给 SQL 语句,或者从 SQL 查询结果中获取数据并转换为 Java 对象时,都会使用 TypeHandler
。
TypeHandler
的主要功能包括:
- Java 类型到 JDBC 类型的转换:在 MyBatis 执行 SQL 语句时,将 Java 对象作为参数传递给 SQL 语句时,
TypeHandler
负责将 Java 类型的数据转换为 JDBC 类型。 - JDBC 类型到 Java 类型的转换:在 MyBatis 从数据库中查询数据时,
TypeHandler
负责将 JDBC 类型的数据转换为 Java 类型。
MyBatis 内置了一些常用的 TypeHandler
,如将 String
转换为 VARCHAR
,将 Integer
转换为 INTEGER
等,满足了大多数情况下的需求。然而,对于一些复杂或自定义类型的转换,可能需要自定义 TypeHandler
。
如何自定义 TypeHandler
处理复杂类型?
当你需要将数据库中的某种数据类型转换为 Java 中的复杂类型,或反过来时,就需要自定义 TypeHandler
。以下是自定义 TypeHandler
的步骤和示例。
1. 实现 TypeHandler
接口
MyBatis 提供了 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 类型。
2. 示例:处理复杂 JSON 数据类型
假设你有一个数据库表,其中有一列存储的是 JSON 格式的字符串,但在 Java 中,你希望将它映射为一个 Map<String, Object>
。
Step 1: 创建一个自定义的 TypeHandler
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, toJson(parameter));
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
return toMap(rs.getString(columnName));
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return toMap(rs.getString(columnIndex));
}
@Override
public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return toMap(cs.getString(columnIndex));
}
private String toJson(Map<String, Object> map) {
try {
return objectMapper.writeValueAsString(map);
} catch (Exception e) {
throw new RuntimeException("Error converting Map to JSON string", e);
}
}
private Map<String, Object> toMap(String json) {
try {
return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
} catch (Exception e) {
throw new RuntimeException("Error converting JSON string to Map", e);
}
}
}
toJson()
:将Map<String, Object>
转换为 JSON 字符串。toMap()
:将 JSON 字符串转换为Map<String, Object>
。
Step 2: 在 MyBatis 配置文件中注册 TypeHandler
你可以在 MyBatis 的配置文件(mybatis-config.xml
)中注册自定义的 TypeHandler
:
<typeHandlers>
<typeHandler handler="com.example.JsonTypeHandler" javaType="java.util.Map" jdbcType="VARCHAR"/>
</typeHandlers>
Step 3: 在 Mapper 中使用 TypeHandler
在你的 Mapper 接口中,你可以直接使用这个 TypeHandler
处理相关的字段:
public interface UserMapper {
@Select("SELECT id, name, extra_info FROM users WHERE id = #{id}")
@Results({
@Result(property = "extraInfo", column = "extra_info", typeHandler = JsonTypeHandler.class)
})
User selectUserById(int id);
}
在这个例子中,数据库中的 extra_info
列是一个 JSON 格式的字符串,而在 Java 对象中,它被映射为 Map<String, Object>
类型。
3. 将 TypeHandler
绑定到具体字段
你也可以在 MyBatis XML 映射文件中,将自定义的 TypeHandler
绑定到具体的字段上:
<resultMap id="UserResultMap" type="com.example.User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="extraInfo" column="extra_info" typeHandler="com.example.JsonTypeHandler"/>
</resultMap>
总结
TypeHandler
的作用:TypeHandler
是 MyBatis 用于在 Java 类型和 JDBC 类型之间进行转换的机制,处理复杂类型的转换时非常有用。- 自定义
TypeHandler
:通过实现TypeHandler
接口,可以定制 MyBatis 如何将复杂类型与数据库字段进行映射。 - 使用场景:自定义
TypeHandler
特别适用于处理复杂的数据类型,如 JSON、枚举、自定义对象等。
通过自定义 TypeHandler
,你可以使 MyBatis 更加灵活地处理复杂的数据类型,满足特定应用场景下的需求。