mybatis的typeHandler 作用

在 MyBatis 中,`TypeHandler`(类型处理器)扮演着 **"翻译官"** 或 **"桥梁"** 的角色。

它的核心作用是:**在 Java 数据类型和 JDBC 数据库数据类型之间进行相互转换。**

因为 Java 的类型(如 `String`, `Date`, `Enum`, 自定义对象)和数据库的类型(如 `VARCHAR`, `TIMESTAMP`, `INT`)并不总是完全一一对应的,`TypeHandler` 就是为了解决这种类型不匹配的问题而存在的。


一、 `TypeHandler` 的两个核心工作方向

`TypeHandler` 的工作是双向的,主要分为"设置参数"和"获取结果"两个过程:

  1. **设置参数(Java -> 数据库)**:

* 当你执行 `INSERT` 或 `UPDATE` 操作时,MyBatis 需要将 Java 对象的属性值设置到 SQL 语句的占位符(`?`)中。

* 此时,`TypeHandler` 负责将 **Java 类型** 转换为 **JDBC 类型**,并调用 `PreparedStatement.setXXX()` 方法存入数据库。

  1. **获取结果(数据库 -> Java)**:

* 当你执行 `SELECT` 操作时,MyBatis 需要从数据库的查询结果集(`ResultSet`)中读取数据,并赋值给 Java 对象。

* 此时,`TypeHandler` 负责将 **JDBC 类型** 转换为 **Java 类型**,并调用 `ResultSet.getXXX()` 方法获取数据。


二、 为什么需要自定义 `TypeHandler`?(常见应用场景)

MyBatis 内部已经默认注册了大量的 `TypeHandler`(如 `StringTypeHandler`, `IntegerTypeHandler`, `DateTypeHandler` 等),能处理 90% 以上的常规映射。**但在以下场景中,你需要自定义 `TypeHandler`:**

1. 数据库存储 JSON,Java 使用对象/集合

* **场景**:数据库字段是 `VARCHAR` 或 `JSON` 类型,存的是 `{"name":"张三","age":18}`。但在 Java 中,你希望直接映射为一个 `UserDetail` 对象或 `List<String>`。

* **解决**:自定义 `TypeHandler`,在入库时用 Jackson/Fastjson 将对象序列化为 JSON 字符串,出库时反序列化为 Java 对象。

2. 枚举类型(Enum)的特殊映射

* **场景**:数据库里存的是枚举的 `code`(如数字 `1` 或字符串 `"MALE"`),但 Java 里使用的是枚举类 `GenderEnum.MALE`。

* **解决**:MyBatis 默认使用枚举的 `name()` 或 `ordinal()` 进行转换。如果数据库存的是自定义的 `code` 属性,就需要自定义 `TypeHandler` 来根据 `code` 查找对应的枚举实例。

3. 数据的加密与解密

* **场景**:数据库中的手机号、身份证号、密码等敏感信息是**加密存储**的(如 AES 密文),但 Java 业务代码中需要使用的是**明文**。

* **解决**:自定义 `TypeHandler`,在 `setNonNullParameter` 时进行加密,在 `getNullableResult` 时进行解密。对业务代码完全透明。

4. 复杂字符串与集合的转换

* **场景**:数据库存的是逗号分隔的字符串 `"1,2,3"`,Java 中希望映射为 `List<Integer>` 或 `Integer\[\]`。

* **解决**:自定义 `TypeHandler`,入库时 `StringUtils.join()`,出库时 `StringUtils.split()`。


三、 如何自定义 `TypeHandler`?

在实际开发中,我们通常不会直接实现 `TypeHandler` 接口(因为方法太多),而是**继承 `BaseTypeHandler<T>`**,它帮我们处理了空值(null)的判断逻辑。

1. 编写自定义 TypeHandler

以 **"数据库存 JSON 字符串,Java 存 Object 对象"** 为例:

```java

// 指定该 Handler 处理的 Java 类型和 JDBC 类型

@MappedTypes(Object.class)

@MappedJdbcTypes(JdbcType.VARCHAR)

public class JsonTypeHandler<T> extends BaseTypeHandler<T> {

private Class<T> clazz;

public JsonTypeHandler(Class<T> clazz) {

if (clazz == null) throw new IllegalArgumentException("Type argument cannot be null");

this.clazz = clazz;

}

// 1. Java -> DB (设置参数)

@Override

public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {

// 将 Java 对象转为 JSON 字符串存入数据库

ps.setString(i, JSON.toJSONString(parameter));

}

// 2. DB -> Java (根据列名获取结果)

@Override

public T getNullableResult(ResultSet rs, String columnName) throws SQLException {

String json = rs.getString(columnName);

return convertJsonToObject(json);

}

// 2. DB -> Java (根据列索引获取结果)

@Override

public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

String json = rs.getString(columnIndex);

return convertJsonToObject(json);

}

// 2. DB -> Java (存储过程获取结果)

@Override

public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {

String json = cs.getString(columnIndex);

return convertJsonToObject(json);

}

private T convertJsonToObject(String json) {

if (json == null || json.isEmpty()) return null;

return JSON.parseObject(json, clazz); // 使用 Fastjson 或 Jackson 反序列化

}

}

```

2. 注册和使用 TypeHandler

写好之后,需要告诉 MyBatis 去使用它。有两种主要方式:

**方式一:全局自动扫描(推荐)**

在 `application.yml` (Spring Boot) 或 `mybatis-config.xml` 中配置包扫描,MyBatis 启动时会自动注册该包下的所有 `TypeHandler`。

```yaml

mybatis:

type-handlers-package: com.yourproject.mybatis.handler

```

**方式二:在 Mapper XML 中显式指定**

如果不想全局生效,可以在具体的 SQL 映射中强制指定使用哪个 `TypeHandler`。

```xml

<!-- 插入/更新时指定 -->

INSERT INTO user (id, extra_info)

VALUES (#{id}, #{extraInfo, typeHandler=com.yourproject.mybatis.handler.JsonTypeHandler})

<!-- 查询结果映射时指定 -->

<resultMap id="userMap" type="User">

<result column="extra_info" property="extraInfo"

typeHandler="com.yourproject.mybatis.handler.JsonTypeHandler"/>

</resultMap>

```

四、 总结

* **本质**:`TypeHandler` 是 Java 类型与 JDBC 类型之间的转换器。

* **默认行为**:MyBatis 内置了数十种常用的 `TypeHandler`,绝大多数基础类型无需手动干预。

* **核心价值**:当遇到 **JSON 映射、枚举特殊映射、数据加解密、格式特殊转换** 等 MyBatis 默认无法处理的场景时,通过自定义 `TypeHandler` 可以在**不侵入业务代码**的前提下,优雅地解决数据持久化问题。

相关推荐
2603_965148113 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
ltl3 小时前
RocksDB 经典故障排查:L0、compaction 与 write stall
数据库
xfhuangfu4 小时前
Oracle中建立到CDB和PDB的连接
数据库·oracle·rpc
小小龙学IT5 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
NineData5 小时前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
J_bean5 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean5 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定
浪子明X6 小时前
从 MongoDB 文档到关系模型:构建可重跑、可对账的数据迁移流水线
数据库·mongodb·oracle
杜子不疼.6 小时前
国产数据库撑起固井软件自主化:金仓 × 中海油服「海恒 Cemsol」落地解析
数据库
隔窗听雨眠6 小时前
GBase 8s并发控制深度解析:封锁机制、隔离级别与死锁处理全攻略
服务器·数据库·oracle