核心思路是让MyBatis在写入数据库前自动加密、查询返回时自动解密,通过继承BaseTypeHandler<String>重写setParameter和三个getResult方法,在数据持久化层拦截特定字段的读写过程,结合AES等工具类实现加解密,并推荐精准注册而非全局注册以避免误伤普通字段。

在 MyBatis 中通过自定义 TypeHandler 实现数据库字段的自动加解密,核心思路是:**让 MyBatis 在写入数据库前自动加密,在查询结果返回时自动解密**。关键在于拦截特定字段的读写过程,而非修改 SQL 或业务层逻辑。
1. 定义加解密工具类(如 AES)
先封装一个安全、可复用的加解密工具,推荐使用 AES/GCM 或 AES/CBC(注意填充和 IV 管理)。务必统一密钥管理方式(如从配置中心或环境变量加载),避免硬编码:
- 密钥长度需合规(如 AES-128 要求 16 字节)
- 加密时生成随机 IV 并与密文一同存储(如拼接为
base64(iv)+":"+base64(ciphertext)) - 解密时先拆分 IV 和密文,再还原明文
- 异常时统一抛出
RuntimeException(MyBatis 会中断执行)
2. 编写自定义 TypeHandler(如 AesEncryptTypeHandler)
继承 BaseTypeHandler<String>,重写四个核心方法:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- setNonNullParameter:参数设入 PreparedStatement 前,对明文 String 加密,再 setString
- getNullableResult(三个重载):从 ResultSet/CallableStatement 中取值后,对密文字符串解密并返回明文
- 注意判空处理(
if (rs.wasNull()) return null;) - 不要在 handler 内做日志或耗时操作,避免影响性能
示例片段:
立即学习“Java免费学习笔记(深入)”;
public class AesEncryptTypeHandler extends BaseTypeHandler<String> {
private final CryptoUtil cryptoUtil = new CryptoUtil(); // 注入或静态实例
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String value, JdbcType jdbcType)
throws SQLException {
String encrypted = cryptoUtil.encrypt(value);
ps.setString(i, encrypted);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
String encrypted = rs.getString(columnName);
return encrypted == null ? null : cryptoUtil.decrypt(encrypted);
}
// ... 其余两个 getNullableResult 重载类似
}
3. 注册 TypeHandler(全局或局部)
两种方式任选其一:
-
全局注册:在 MyBatis 配置文件(mybatis-config.xml)中添加:
<typeHandlers> <typeHandler handler="com.example.AesEncryptTypeHandler" javaType="java.lang.String" /> </typeHandlers>
此时所有 String 类型字段都会走该 handler —— 不推荐,易误伤普通字段 -
精准注册(推荐):仅对需要加密的字段显式指定。在
@Select/@Insert等注解中用resultMap或直接在 XML 的<result column="phone" property="phone" typeHandler="com.example.AesEncryptTypeHandler"/>中声明 - 也可在实体类字段上加
@TypeHandler(AesEncryptTypeHandler.class)注解(需开启注解扫描)
4. 注意事项与避坑点
-
字段类型一致性:数据库列类型建议用
VARCHAR(足够存 base64 密文),避免 BLOB 增加复杂度 - 索引与查询限制:加密后字段无法直接用于 LIKE、ORDER BY、WHERE 条件匹配(除非使用确定性加密+盲索引,但属进阶场景)
- 空值与空字符串:加密空字符串得到的是有效密文,解密后仍是空串;null 则保持 null —— 需明确业务语义
- 事务与缓存:加解密发生在 MyBatis 执行层,不影响一级/二级缓存行为;但密文变化会导致缓存 key 变化
- 测试覆盖:必须验证 insert → select 全链路,确保加解密可逆且无乱码、截断

















