欢迎光临
我们一直在努力

让代码更优雅?Mybatis 类型处理器了解一下!

一、什么是类型处理器(TypeHandler)?

1.1 核心概念

类型处理器是 MyBatis 中用于处理 Java 类型与 JDBC 类型之间转换的核心组件。它解决了两个关键问题:

  • 参数映射:将 Java 对象转换为 JDBC 参数

  • 结果映射:将 ResultSet 中的数据转换为 Java 对象

  • java

    // 简化的类型处理器接口
    public interface TypeHandler<T> {
    // 设置 PreparedStatement 参数
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType);

    // 从 ResultSet 获取结果
    T getResult(ResultSet rs, String columnName);
    T getResult(ResultSet rs, int columnIndex);
    T getResult(CallableStatement cs, int columnIndex);
    }

    1.2 为什么需要类型处理器?

    问题场景:

    java

    // 传统的做法:在业务代码中处理类型转换
    public class UserMapper {
    public User findById(Long id) {
    User user = sqlSession.selectOne("findUserById", id);

    // 手动转换 JSON 字段
    String jsonAttr = user.getJsonAttr();
    Map<String, Object> attributes = JSON.parse(jsonAttr);
    user.setAttributes(attributes);

    // 手动转换枚举
    String statusStr = user.getStatus();
    UserStatus status = UserStatus.valueOf(statusStr);
    user.setStatusEnum(status);

    return user;
    }
    }

    使用类型处理器后:

    java

    public class UserMapper {
    public User findById(Long id) {
    // 所有转换自动完成
    return sqlSession.selectOne("findUserById", id);
    }
    }

    二、MyBatis 内置类型处理器

    2.1 常用内置处理器

    Java 类型JDBC 类型类型处理器
    String VARCHAR, CHAR StringTypeHandler
    Integer INTEGER IntegerTypeHandler
    Long BIGINT LongTypeHandler
    Date TIMESTAMP DateTypeHandler
    BigDecimal DECIMAL BigDecimalTypeHandler
    boolean BOOLEAN, BIT BooleanTypeHandler
    byte[] BLOB, BINARY BlobTypeHandler
    Enum VARCHAR EnumTypeHandler
    Enum INTEGER EnumOrdinalTypeHandler

    2.2 枚举处理器示例

    java

    // 枚举定义
    public enum UserStatus {
    ACTIVE("A", "活跃"),
    INACTIVE("I", "禁用"),
    LOCKED("L", "锁定");

    private final String code;
    private final String description;

    UserStatus(String code, String description) {
    this.code = code;
    this.description = description;
    }

    public String getCode() {
    return code;
    }

    public static UserStatus fromCode(String code) {
    for (UserStatus status : values()) {
    if (status.code.equals(code)) {
    return status;
    }
    }
    throw new IllegalArgumentException("未知状态码: " + code);
    }
    }

    // 自动使用 EnumTypeHandler(按名称存储)
    // 数据库中存储 "ACTIVE", "INACTIVE" 等字符串

    三、自定义类型处理器实战

    3.1 继承 BaseTypeHandler

    java

    // JSON 类型处理器示例
    @MappedTypes(Map.class) // 处理的 Java 类型
    @MappedJdbcTypes(JdbcType.VARCHAR) // 处理的 JDBC 类型
    public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
    // 配置 ObjectMapper
    OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
    try {
    String json = OBJECT_MAPPER.writeValueAsString(parameter);
    ps.setString(i, json);
    } catch (JsonProcessingException e) {
    throw new SQLException("JSON 序列化失败", e);
    }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String json = rs.getString(columnName);
    return parseJson(json);
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    String json = rs.getString(columnIndex);
    return parseJson(json);
    }

    @Override
    public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    String json = cs.getString(columnIndex);
    return parseJson(json);
    }

    private Map<String, Object> parseJson(String json) {
    if (StringUtils.isBlank(json)) {
    return new HashMap<>();
    }
    try {
    return OBJECT_MAPPER.readValue(json,
    new TypeReference<Map<String, Object>>() {});
    } catch (IOException e) {
    throw new RuntimeException("JSON 解析失败: " + json, e);
    }
    }
    }

    3.2 枚举代码映射处理器

    java

    // 自定义枚举处理器:按代码存储
    public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    UserStatus parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter.getCode());
    }

    @Override
    public UserStatus getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String code = rs.getString(columnName);
    return rs.wasNull() ? null : UserStatus.fromCode(code);
    }

    @Override
    public UserStatus getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    String code = rs.getString(columnIndex);
    return rs.wasNull() ? null : UserStatus.fromCode(code);
    }

    @Override
    public UserStatus getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    String code = cs.getString(columnIndex);
    return cs.wasNull() ? null : UserStatus.fromCode(code);
    }
    }

    3.3 日期时间处理器

    java

    // Java 8 时间处理器
    public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
    ps.setTimestamp(i, Timestamp.valueOf(parameter));
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    Timestamp timestamp = rs.getTimestamp(columnName);
    return timestamp != null ? timestamp.toLocalDateTime() : null;
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    Timestamp timestamp = rs.getTimestamp(columnIndex);
    return timestamp != null ? timestamp.toLocalDateTime() : null;
    }

    @Override
    public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    Timestamp timestamp = cs.getTimestamp(columnIndex);
    return timestamp != null ? timestamp.toLocalDateTime() : null;
    }
    }

    3.4 集合类型处理器

    java

    // 逗号分隔的字符串转 List
    public class StringListTypeHandler extends BaseTypeHandler<List<String>> {

    private static final String DELIMITER = ",";

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    List<String> parameter, JdbcType jdbcType) throws SQLException {
    String value = String.join(DELIMITER, parameter);
    ps.setString(i, value);
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String value = rs.getString(columnName);
    return parseString(value);
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    String value = rs.getString(columnIndex);
    return parseString(value);
    }

    @Override
    public List<String> getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    String value = cs.getString(columnIndex);
    return parseString(value);
    }

    private List<String> parseString(String value) {
    if (StringUtils.isBlank(value)) {
    return new ArrayList<>();
    }
    return Arrays.asList(value.split(DELIMITER));
    }
    }

    四、配置类型处理器

    4.1 XML 配置方式

    xml

    <!– mybatis-config.xml –>
    <configuration>
    <typeHandlers>
    <!– 方式1:直接指定类 –>
    <typeHandler handler="com.example.handler.JsonTypeHandler"/>

    <!– 方式2:指定Java类型和处理器 –>
    <typeHandler
    javaType="java.util.Map"
    handler="com.example.handler.JsonTypeHandler"/>

    <!– 方式3:完整配置 –>
    <typeHandler
    javaType="java.util.Map"
    jdbcType="VARCHAR"
    handler="com.example.handler.JsonTypeHandler"/>

    <!– 方式4:包扫描 –>
    <package name="com.example.handler"/>
    </typeHandlers>
    </configuration>

    4.2 Spring Boot 配置

    java

    // 配置类方式
    @Configuration
    public class MybatisConfig {

    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer() {
    return configuration -> {
    // 注册类型处理器
    configuration.getTypeHandlerRegistry().register(
    Map.class,
    JdbcType.VARCHAR,
    JsonTypeHandler.class
    );

    // 注册枚举处理器
    configuration.getTypeHandlerRegistry().register(
    UserStatus.class,
    UserStatusTypeHandler.class
    );
    };
    }
    }

    // 或者在 MapperScan 中指定
    @MapperScan(
    basePackages = "com.example.mapper",
    typeHandlers = {JsonTypeHandler.class, UserStatusTypeHandler.class}
    )

    4.3 注解配置

    java

    // 在实体类字段上直接指定
    public class User {
    private Long id;
    private String name;

    @TableField(typeHandler = JsonTypeHandler.class)
    private Map<String, Object> attributes;

    @TableField(typeHandler = UserStatusTypeHandler.class)
    private UserStatus status;

    // getters and setters
    }

    五、在 Mapper 中使用类型处理器

    5.1 XML Mapper 中使用

    xml

    <!– UserMapper.xml –>
    <mapper namespace="com.example.mapper.UserMapper">

    <!– 结果映射中使用 –>
    <resultMap id="userMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="attributes" column="attributes"
    typeHandler="com.example.handler.JsonTypeHandler"/>
    <result property="status" column="status"
    typeHandler="com.example.handler.UserStatusTypeHandler"/>
    </resultMap>

    <!– 查询语句 –>
    <select id="selectById" resultMap="userMap">
    SELECT * FROM users WHERE id = #{id}
    </select>

    <!– 插入语句 –>
    <insert id="insert" parameterType="User">
    INSERT INTO users (name, attributes, status)
    VALUES (
    #{name},
    #{attributes, typeHandler=com.example.handler.JsonTypeHandler},
    #{status, typeHandler=com.example.handler.UserStatusTypeHandler}
    )
    </insert>

    <!– 更新语句 –>
    <update id="update" parameterType="User">
    UPDATE users SET
    name = #{name},
    attributes = #{attributes, typeHandler=com.example.handler.JsonTypeHandler},
    status = #{status, typeHandler=com.example.handler.UserStatusTypeHandler}
    WHERE id = #{id}
    </update>
    </mapper>

    5.2 注解方式使用

    java

    @Mapper
    public interface UserMapper {

    @Results({
    @Result(column = "id", property = "id"),
    @Result(column = "name", property = "name"),
    @Result(column = "attributes", property = "attributes",
    typeHandler = JsonTypeHandler.class),
    @Result(column = "status", property = "status",
    typeHandler = UserStatusTypeHandler.class)
    })
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(Long id);

    @Insert("INSERT INTO users (name, attributes, status) " +
    "VALUES (#{name}, " +
    "#{attributes, typeHandler=com.example.handler.JsonTypeHandler}, " +
    "#{status, typeHandler=com.example.handler.UserStatusTypeHandler})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);
    }

    六、高级应用场景

    6.1 多数据库适配

    java

    // 支持多种数据库的 JSON 处理
    public class MultiDatabaseJsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    private DatabaseType databaseType;
    private ObjectMapper objectMapper = new ObjectMapper();

    public MultiDatabaseJsonTypeHandler() {
    // 可以通过配置文件或自动检测获取数据库类型
    this.databaseType = detectDatabaseType();
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {

    try {
    String json = objectMapper.writeValueAsString(parameter);

    switch (databaseType) {
    case MYSQL:
    // MySQL 5.7+ 支持 JSON 类型
    ps.setObject(i, json, Types.OTHER);
    break;
    case POSTGRESQL:
    // PostgreSQL 使用 jsonb
    ps.setObject(i, json, Types.OTHER);
    break;
    case ORACLE:
    // Oracle 使用 CLOB 或 VARCHAR2
    if (json.length() > 4000) {
    ps.setClob(i, new StringReader(json));
    } else {
    ps.setString(i, json);
    }
    break;
    default:
    ps.setString(i, json);
    }
    } catch (JsonProcessingException e) {
    throw new SQLException("JSON 序列化失败", e);
    }
    }

    // 省略其他方法…
    }

    6.2 加密字段处理

    java

    // 数据库字段加密处理器
    public class EncryptTypeHandler extends BaseTypeHandler<String> {

    private static final String AES_KEY = "your-secret-key";
    private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    String parameter, JdbcType jdbcType) throws SQLException {
    try {
    String encrypted = encrypt(parameter);
    ps.setString(i, encrypted);
    } catch (Exception e) {
    throw new SQLException("加密失败", e);
    }
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String encrypted = rs.getString(columnName);
    return encrypted != null ? decrypt(encrypted) : null;
    }

    private String encrypt(String data) throws Exception {
    Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE,
    new SecretKeySpec(AES_KEY.getBytes(), "AES"));
    byte[] encrypted = cipher.doFinal(data.getBytes());
    return Base64.getEncoder().encodeToString(encrypted);
    }

    private String decrypt(String encrypted) {
    try {
    Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE,
    new SecretKeySpec(AES_KEY.getBytes(), "AES"));
    byte[] decoded = Base64.getDecoder().decode(encrypted);
    byte[] decrypted = cipher.doFinal(decoded);
    return new String(decrypted);
    } catch (Exception e) {
    throw new RuntimeException("解密失败", e);
    }
    }
    }

    6.3 通用泛型处理器

    java

    // 通用 JSON 处理器,支持任意类型
    public class GenericJsonTypeHandler<T> extends BaseTypeHandler<T> {

    private final Class<T> type;
    private final ObjectMapper objectMapper;

    public GenericJsonTypeHandler(Class<T> type) {
    this.type = type;
    this.objectMapper = new ObjectMapper();
    this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    T parameter, JdbcType jdbcType) throws SQLException {
    try {
    String json = objectMapper.writeValueAsString(parameter);
    ps.setString(i, json);
    } catch (JsonProcessingException e) {
    throw new SQLException("JSON 序列化失败", e);
    }
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String json = rs.getString(columnName);
    return parseJson(json);
    }

    private T parseJson(String json) {
    if (StringUtils.isBlank(json)) {
    return null;
    }
    try {
    return objectMapper.readValue(json, type);
    } catch (IOException e) {
    throw new RuntimeException("JSON 解析失败: " + json, e);
    }
    }

    // 省略其他 getResult 方法
    }

    6.4 注册泛型处理器

    java

    // 自定义 TypeHandlerRegistry
    @Component
    public class CustomTypeHandlerRegistry {

    @PostConstruct
    public void registerGenericHandlers() {
    // 创建通用处理器实例
    TypeHandler<Map<String, Object>> mapHandler =
    new GenericJsonTypeHandler<>(Map.class);
    TypeHandler<List<String>> listHandler =
    new GenericJsonTypeHandler<>(List.class);
    TypeHandler<UserConfig> configHandler =
    new GenericJsonTypeHandler<>(UserConfig.class);

    // 注册到 MyBatis
    Configuration configuration = getMybatisConfiguration();
    configuration.getTypeHandlerRegistry().register(
    Map.class, JdbcType.VARCHAR, mapHandler);
    configuration.getTypeHandlerRegistry().register(
    List.class, JdbcType.VARCHAR, listHandler);
    configuration.getTypeHandlerRegistry().register(
    UserConfig.class, JdbcType.VARCHAR, configHandler);
    }
    }

    七、最佳实践和注意事项

    7.1 性能优化

    java

    // 使用缓存提升性能
    public class CachedJsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final ConcurrentHashMap<String, Map<String, Object>> CACHE =
    new ConcurrentHashMap<>();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
    try {
    String json = MAPPER.writeValueAsString(parameter);
    ps.setString(i, json);
    } catch (JsonProcessingException e) {
    throw new SQLException("JSON 序列化失败", e);
    }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String json = rs.getString(columnName);
    if (json == null) {
    return null;
    }

    // 缓存命中
    Map<String, Object> cached = CACHE.get(json);
    if (cached != null) {
    return cached;
    }

    // 解析并缓存
    Map<String, Object> result = parseJson(json);
    if (result != null) {
    CACHE.put(json, result);
    }
    return result;
    }

    // 省略其他方法…
    }

    7.2 异常处理

    java

    // 增强的异常处理
    public class SafeJsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
    try {
    String json = MAPPER.writeValueAsString(parameter);
    ps.setString(i, json);
    } catch (JsonProcessingException e) {
    // 记录日志但不抛出异常,使用空对象替代
    log.error("JSON 序列化失败,使用空对象", e);
    try {
    ps.setString(i, "{}");
    } catch (SQLException ex) {
    throw new SQLException("设置参数失败", ex);
    }
    }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String json = rs.getString(columnName);
    if (StringUtils.isBlank(json)) {
    return new HashMap<>();
    }

    try {
    return MAPPER.readValue(json,
    new TypeReference<Map<String, Object>>() {});
    } catch (IOException e) {
    log.warn("JSON 解析失败,返回空对象: {}", json, e);
    return new HashMap<>();
    }
    }

    // 省略其他方法…
    }

    7.3 单元测试

    java

    // 类型处理器单元测试
    @SpringBootTest
    public class JsonTypeHandlerTest {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Test
    public void testJsonTypeHandler() throws Exception {
    try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);

    // 准备测试数据
    User user = new User();
    user.setName("张三");
    Map<String, Object> attributes = new HashMap<>();
    attributes.put("age", 25);
    attributes.put("city", "北京");
    attributes.put("hobbies", Arrays.asList("篮球", "音乐"));
    user.setAttributes(attributes);

    // 插入数据
    mapper.insert(user);
    session.commit();

    // 查询数据
    User retrieved = mapper.selectById(user.getId());

    // 验证
    assertNotNull(retrieved);
    assertEquals("张三", retrieved.getName());
    assertEquals(25, retrieved.getAttributes().get("age"));
    assertEquals("北京", retrieved.getAttributes().get("city"));

    // 验证 JSON 序列化/反序列化
    String json = new ObjectMapper().writeValueAsString(attributes);
    Map<String, Object> fromDb = retrieved.getAttributes();
    String jsonFromDb = new ObjectMapper().writeValueAsString(fromDb);
    assertEquals(json, jsonFromDb);
    }
    }

    @Test
    public void testNullHandling() {
    JsonTypeHandler handler = new JsonTypeHandler();

    // 测试空值处理
    assertNull(handler.getNullableResult(null, 0));

    // 测试空字符串
    Map<String, Object> result = handler.parseJson("");
    assertNotNull(result);
    assertTrue(result.isEmpty());
    }
    }

    八、Spring Boot 集成示例

    8.1 完整项目结构

    text

    src/main/java/
    ├── com.example.demo/
    │ ├── config/
    │ │ ├── MybatisConfig.java
    │ │ └── TypeHandlerConfig.java
    │ ├── handler/
    │ │ ├── JsonTypeHandler.java
    │ │ ├── EncryptTypeHandler.java
    │ │ └── UserStatusTypeHandler.java
    │ ├── entity/
    │ │ ├── User.java
    │ │ ├── UserStatus.java
    │ │ └── BaseEntity.java
    │ ├── mapper/
    │ │ └── UserMapper.java
    │ ├── service/
    │ │ └── UserService.java
    │ └── DemoApplication.java

    8.2 配置类

    java

    // TypeHandlerConfig.java
    @Configuration
    public class TypeHandlerConfig {

    @Bean
    @ConfigurationProperties(prefix = "mybatis.configuration")
    public org.apache.ibatis.session.Configuration mybatisConfiguration() {
    org.apache.ibatis.session.Configuration configuration =
    new org.apache.ibatis.session.Configuration();

    // 注册类型处理器
    TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();

    // 注册自定义处理器
    registry.register(JsonTypeHandler.class);
    registry.register(EncryptTypeHandler.class);
    registry.register(UserStatus.class, UserStatusTypeHandler.class);

    // 注册通用处理器
    registry.register(Map.class, JdbcType.VARCHAR,
    new GenericJsonTypeHandler<>(Map.class));
    registry.register(List.class, JdbcType.VARCHAR,
    new GenericJsonTypeHandler<>(List.class));

    return configuration;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(
    DataSource dataSource,
    org.apache.ibatis.session.Configuration configuration)
    throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setConfiguration(configuration);

    // 设置类型别名包
    factory.setTypeAliasesPackage("com.example.demo.entity");

    // 设置类型处理器包
    factory.setTypeHandlersPackage("com.example.demo.handler");

    return factory.getObject();
    }
    }

    8.3 实体类示例

    java

    // User.java
    @Data
    @TableName("users")
    public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;

    @TableField(typeHandler = JsonTypeHandler.class)
    private Map<String, Object> attributes;

    @TableField(typeHandler = EncryptTypeHandler.class)
    private String phone;

    @TableField(typeHandler = UserStatusTypeHandler.class)
    private UserStatus status;

    @TableField(typeHandler = LocalDateTimeTypeHandler.class)
    private LocalDateTime createTime;

    @TableField(typeHandler = LocalDateTimeTypeHandler.class)
    private LocalDateTime updateTime;
    }

    8.4 服务层使用

    java

    // UserService.java
    @Service
    public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User createUser(UserDTO userDTO) {
    User user = new User();
    user.setName(userDTO.getName());

    // 设置属性,类型处理器会自动转换
    Map<String, Object> attributes = new HashMap<>();
    attributes.put("age", userDTO.getAge());
    attributes.put("email", userDTO.getEmail());
    attributes.put("preferences", userDTO.getPreferences());
    user.setAttributes(attributes);

    // 敏感信息自动加密
    user.setPhone(userDTO.getPhone());

    // 枚举自动转换
    user.setStatus(UserStatus.ACTIVE);

    userMapper.insert(user);
    return user;
    }

    public User getUserById(Long id) {
    // 所有转换在 MyBatis 层自动完成
    return userMapper.selectById(id);
    }
    }

    九、常见问题与解决方案

    9.1 问题1:类型处理器不生效

    原因分析:

  • 未正确注册类型处理器

  • Mapper XML 中未指定 typeHandler

  • Java 类型与处理器不匹配

  • 解决方案:

    java

    // 调试代码
    @SpringBootTest
    public class TypeHandlerDebugTest {

    @Test
    public void debugTypeHandler() {
    Configuration configuration = sqlSessionFactory.getConfiguration();
    TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();

    // 检查处理器是否注册
    TypeHandler<?> handler = registry.getTypeHandler(Map.class, JdbcType.VARCHAR);
    System.out.println("注册的处理器: " + handler);

    // 检查 MappedStatement
    MappedStatement ms = configuration.getMappedStatement(
    "com.example.mapper.UserMapper.selectById");
    ResultMap resultMap = ms.getResultMaps().get(0);

    for (ResultMapping mapping : resultMap.getPropertyResultMappings()) {
    System.out.println("字段: " + mapping.getProperty() +
    ", 处理器: " + mapping.getTypeHandler());
    }
    }
    }

    9.2 问题2:性能问题

    优化策略:

  • 使用缓存

  • 避免在循环中创建 ObjectMapper

  • 使用连接池管理 PreparedStatement

  • java

    // 性能优化示例
    public class OptimizedJsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    // 使用静态变量避免重复创建
    private static final ThreadLocal<ObjectMapper> MAPPER_CACHE =
    ThreadLocal.withInitial(() -> {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    return mapper;
    });

    // 使用软引用缓存解析结果
    private static final Map<String, SoftReference<Map<String, Object>>> CACHE =
    new ConcurrentHashMap<>();

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    String json = rs.getString(columnName);
    if (json == null) {
    return null;
    }

    // 检查缓存
    SoftReference<Map<String, Object>> ref = CACHE.get(json);
    if (ref != null) {
    Map<String, Object> cached = ref.get();
    if (cached != null) {
    return cached;
    }
    }

    // 解析并缓存
    try {
    ObjectMapper mapper = MAPPER_CACHE.get();
    Map<String, Object> result = mapper.readValue(json,
    new TypeReference<Map<String, Object>>() {});

    CACHE.put(json, new SoftReference<>(result));
    return result;
    } catch (IOException e) {
    throw new SQLException("JSON 解析失败", e);
    }
    }
    }

    9.3 问题3:版本兼容性

    java

    // 多版本兼容的处理器
    public class VersionAwareTypeHandler<T> extends BaseTypeHandler<T> {

    private final Map<String, TypeHandler<T>> versionHandlers = new HashMap<>();

    public VersionAwareTypeHandler() {
    // 注册不同版本的处理器
    versionHandlers.put("1.0", new V1TypeHandler());
    versionHandlers.put("2.0", new V2TypeHandler());
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
    T parameter, JdbcType jdbcType) throws SQLException {
    String version = getCurrentVersion();
    TypeHandler<T> handler = versionHandlers.get(version);
    if (handler != null) {
    handler.setParameter(ps, i, parameter, jdbcType);
    } else {
    // 使用默认处理器
    ps.setObject(i, parameter);
    }
    }

    private String getCurrentVersion() {
    // 从配置文件或上下文获取版本号
    return "2.0";
    }
    }

    赞(0)
    未经允许不得转载:171主机测评 » 让代码更优雅?Mybatis 类型处理器了解一下!
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址