2025/12/28 19:54:45
网站建设
项目流程
网站购物系统制作雨辰资讯电子商务类网站开发,北京建设工程招标信息网,返利淘网站怎么做,网站子页面如何做seo前情回顾#xff1a;
在 《MyBatis基础入门《十》Spring Boot 整合 MyBatis》 中#xff0c;我们完成了企业级项目的基础搭建。
但现实业务中#xff0c;数据库字段和 Java 对象往往 不是简单的一一对应#xff1a;MySQL 的 JSON 字段要映射为 MapString, Object 或…前情回顾在 《MyBatis基础入门《十》Spring Boot 整合 MyBatis》 中我们完成了企业级项目的基础搭建。但现实业务中数据库字段和 Java 对象往往不是简单的一一对应MySQL 的JSON字段要映射为MapString, Object或自定义对象枚举值如OrderStatus.PAID需存为数字或字符串敏感字段手机号、身份证入库前加密查询后解密。如何统一、安全、高效地处理这些转换答案使用MyBatis TypeHandler本文将从原理到实战手把手教你编写自定义类型处理器。一、什么是 TypeHandlerTypeHandler 是 MyBatis 提供的类型转换器负责Java Type → JDBC Type设置参数时如PreparedStatement.setXXX()JDBC Type → Java Type获取结果时如ResultSet.getXXX()MyBatis 内置了大量 TypeHandler如StringTypeHandler,IntegerTypeHandler但面对复杂类型时我们需要自定义。二、实战场景一MySQL JSON 字段 ↔ Java 对象场景说明用户表中有一个profile JSON字段存储用户扩展信息CREATE TABLE tbl_user ( id INT PRIMARY KEY, username VARCHAR(50), profile JSON );Java 实体public class User { private Integer id; private String username; private UserProfile profile; // 自定义对象 } public class UserProfile { private String avatar; private String city; // getter / setter }目标查询时自动将 JSON 字符串转为UserProfile插入时反向转换。步骤 1引入 JSON 工具如 Jacksondependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency步骤 2编写自定义 TypeHandler// JsonTypeHandler.java package com.charles.typehandler; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.*; public class JsonTypeHandlerT extends BaseTypeHandlerT { private static final ObjectMapper objectMapper new ObjectMapper(); private ClassT type; public JsonTypeHandler(ClassT type) { if (type null) throw new IllegalArgumentException(Type argument cannot be null); this.type type; } Override public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { try { String json objectMapper.writeValueAsString(parameter); ps.setString(i, json); // 存为 VARCHAR/TEXT } catch (Exception e) { throw new SQLException(Error converting type.getSimpleName() to JSON, e); } } Override public T getNullableResult(ResultSet rs, String columnName) throws SQLException { return parseJson(rs.getString(columnName)); } Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parseJson(rs.getString(columnIndex)); } Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parseJson(cs.getString(columnIndex)); } private T parseJson(String json) throws SQLException { if (json null || json.isEmpty()) return null; try { return objectMapper.readValue(json, type); } catch (Exception e) { throw new SQLException(Error parsing JSON to type.getSimpleName(), e); } } }✅ 继承BaseTypeHandlerT实现四个核心方法 ✅ 使用泛型支持任意 Java 对象 ✅ 异常统一包装为SQLException。步骤 3在实体类中注册 TypeHandlerpublic class User { private Integer id; private String username; Results({ Result(property profile, column profile, typeHandler JsonTypeHandler.class) }) private UserProfile profile; }或更简洁地在字段上使用TypeHandlerMyBatis 3.4public class User { // ... TypeHandler(JsonTypeHandler.class) private UserProfile profile; } 注意若使用 XML 映射可在result标签中指定typeHandler。步骤 4测试效果Test public void testJsonTypeHandler() { User user new User(); user.setUsername(张三); UserProfile profile new UserProfile(); profile.setAvatar(avatar.jpg); profile.setCity(深圳); user.setProfile(profile); userMapper.insert(user); // 自动转为 JSON 字符串存入数据库 User saved userMapper.selectById(user.getId()); System.out.println(saved.getProfile().getCity()); // 输出深圳 }✅ 成功无需手动序列化/反序列化三、实战场景二枚举存储Enum ↔ Integer/String需求订单状态CREATED0,PAID1,SHIPPED2public enum OrderStatus { CREATED(0), PAID(1), SHIPPED(2); private final int code; OrderStatus(int code) { this.code code; } public int getCode() { return code; } public static OrderStatus fromCode(int code) { for (OrderStatus s : values()) { if (s.code code) return s; } throw new IllegalArgumentException(Invalid code: code); } }自定义 EnumTypeHandlerpublic class OrderStatusTypeHandler extends BaseTypeHandlerOrderStatus { Override public void setNonNullParameter(PreparedStatement ps, int i, OrderStatus parameter, JdbcType jdbcType) throws SQLException { ps.setInt(i, parameter.getCode()); } Override public OrderStatus getNullableResult(ResultSet rs, String columnName) throws SQLException { int code rs.getInt(columnName); return rs.wasNull() ? null : OrderStatus.fromCode(code); } // ... 其他 getNullableResult 方法类似 }在Order实体中使用TypeHandler(OrderStatusTypeHandler.class) private OrderStatus status; 优势数据库存整数Java 用枚举安全又语义清晰四、全局注册 TypeHandler可选避免每个字段重复声明可在mybatis-config.xml中全局注册typeHandlers typeHandler handlercom.charles.typehandler.JsonTypeHandler javaTypecom.charles.entity.UserProfile jdbcTypeVARCHAR / /typeHandlers或在 Spring Boot 中通过配置类Configuration public class MyBatisConfig { Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory new SqlSessionFactoryBean(); factory.setDataSource(dataSource); // 注册全局 TypeHandler TypeHandlerRegistry registry factory.getObject().getConfiguration().getTypeHandlerRegistry(); registry.register(UserProfile.class, new JsonTypeHandler(UserProfile.class)); return factory.getObject(); } }五、注意事项 最佳实践⚠️ 1. 线程安全ObjectMapper是线程安全的Jackson 2.8可共享避免在 TypeHandler 中使用非线程安全的成员变量。⚠️ 2. 异常处理必须捕获内部异常并转为SQLException否则 MyBatis 无法正确回滚。✅ 3. 性能TypeHandler 在每次 SQL 执行时调用避免做耗时操作可缓存反射结果如枚举 code 映射。 4. 与 JSON 数据库类型兼容MySQL 5.7 支持JSON类型但 JDBC 驱动仍将其视为VARCHAR因此setString/getString完全适用。六、总结TypeHandler 应用场景速查场景解决方案JSON 字段 ↔ 对象自定义JsonTypeHandler枚举 ↔ 数字/字符串自定义EnumTypeHandler加密字段如手机号入库加密出库解密日期格式定制如只存年月自定义LocalDateTypeHandler多值字段如逗号分隔转为ListString✨核心价值“让数据库字段与 Java 对象自由对话代码更干净逻辑更内聚”