1.BaseTypeHandler简介
org.apache.ibatis.type.BaseTypeHandler
是 MyBatis 提供的一个抽象类,通过继承该类并实现关键方法,可用于实现 Java 类型 与 JDBC 类型 之间的双向转换。当数据库字段类型与 Java 对象属性类型不一致时(如:枚举类型、自定义对象、JSON 字段等),可以通过自定义 BaseTypeHandler
实现灵活的数据类型映射。
1.1 核心方法
BaseTypeHandler<T>
是泛型类,其中 T
表示 Java 类型。需要实现以下 4 个核心方法:
方法 | 作用 |
---|---|
void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) |
将 Java 类型转换为 JDBC 类型,设置到 SQL 语句中 |
T getNullableResult(ResultSet rs, String columnName) |
从 ResultSet 中通过列名获取 Java 类型值 |
T getNullableResult(ResultSet rs, int columnIndex) |
从 ResultSet 中通过列索引获取 Java 类型值 |
T getNullableResult(CallableStatement cs, int columnIndex) |
从存储过程结果中通过列索引获取 Java 类型值 |
1.2 典型使用场景
- 枚举类型映射:将数据库中的字符串或整数映射为 Java 枚举。
- 复杂对象映射:将 JSON 字符串转换为 Java 对象(如
List<T>
或自定义类)。 - 特殊类型转换:将数据库中的
DATE
/TIMESTAMP
映射为LocalDateTime
等。
2. 使用示例
2.1 枚举类型转换
将自定义的枚举类型与数据库里的String类型做自动转换。
step1.定义枚举类
public enum Status {
ACTIVE("active"),
INACTIVE("inactive");
private final String code;
Status(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Status fromCode(String code) {
for (Status status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Invalid code: " + code);
}
}
step2.自定义 TypeHandler,用于将枚举 Status 类和数据库中的String类做转换
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StatusTypeHandler extends BaseTypeHandler<Status> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Status parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.getCode());
}
@Override
public Status getNullableResult(ResultSet rs, String columnName) throws SQLException {
String code = rs.getString(columnName);
return code == null ? null : Status.fromCode(code);
}
@Override
public Status getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String code = rs.getString(columnIndex);
return code == null ? null : Status.fromCode(code);
}
@Override
public Status getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String code = cs.getString(columnIndex);
return code == null ? null : Status.fromCode(code);
}
}
step3. 在XML映射文件中使用
- status在数据库users表里是一个String类型字符串;在 User类里是一个 Status 枚举类型对象
- 经过StatusTypeHandler处理后,可以将数据库的String类型映射为User里的Status枚举类型
<!-- UserMapper.xml -->
<resultMap id="userResultMap" type="User">
<result column="status" property="status" typeHandler="com.example.StatusTypeHandler"/>
</resultMap>
<select id="selectUser" resultMap="userResultMap">
SELECT * FROM users WHERE id = #{id}
</select>
2.2 JSON类型转换
将JSONObject类型与数据库里的String类型做转换:
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.JSONObject;
public class JSONObjectTypeHandler extends BaseTypeHandler<JSONObject> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter == null ? "{}" : parameter.toJSONString());
}
@Override
public JSONObject getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return StringUtils.isBlank(json) ? new JSONObject() : JSONObject.parseObject(json);
}
@Override
public JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
return StringUtils.isBlank(json) ? new JSONObject() : JSONObject.parseObject(json);
}
@Override
public JSONObject getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
return StringUtils.isBlank(json) ? new JSONObject() : JSONObject.parseObject(json);
}
}
2.3 注意事项
- 空值处理:在
getNullableResult
中需判断null
,避免 NPE。 - 线程安全:避免在
TypeHandler
中使用可变成员变量。 - 性能优化:避免在转换过程中频繁创建对象(如 JSON 序列化器)。