首页 新能源汽车

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南

字数: (1471)
阅读: (7080)
内容摘要:MyBatis-Plus 字段类型处理器深度解析与实战避坑指南,

在使用 MyBatis-Plus 进行数据库操作时,经常会遇到数据库字段类型与 Java 实体类属性类型不一致的问题。例如,数据库中存储的是 JSON 字符串,而 Java 实体类需要的是一个 List 或 Map。这时,MyBatis-Plus 提供的字段类型处理器(TypeHandler)就能派上用场,它可以在 MyBatis 自动映射结果集的过程中,实现类型转换,避免手动编写大量的 XML 配置。

常见的字段类型转换场景

  1. JSON 字符串与 List/Map 的互转:数据库字段存储 JSON 格式的数据,Java 实体类属性定义为 List 或 Map 类型。
  2. 枚举类型与数据库字符串/数字的互转:数据库字段存储枚举类型的字符串或数字值,Java 实体类属性定义为枚举类型。
  3. 日期类型处理:处理数据库中时间戳、日期字符串与 Java Date/LocalDateTime 等类型的转换。

MyBatis-Plus TypeHandler 底层原理

MyBatis-Plus 的 TypeHandler 实际上是对 MyBatis 原生 TypeHandler 的扩展和封装。MyBatis 在执行 SQL 查询后,会通过 ResultSetExtractor 将结果集映射到 Java 对象。在这个映射过程中,如果遇到需要类型转换的字段,就会调用相应的 TypeHandler 进行处理。

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南

TypeHandler 的核心方法包括:

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南
  • setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType):将 Java 类型的参数转换为 JDBC 类型,用于 SQL 语句的参数绑定。
  • getResult(ResultSet rs, String columnName):从 ResultSet 中获取指定列的值,并转换为 Java 类型。
  • getResult(ResultSet rs, int columnIndex):从 ResultSet 中获取指定索引的列的值,并转换为 Java 类型。
  • getResult(CallableStatement cs, int columnIndex):从 CallableStatement 中获取指定索引的参数值,并转换为 Java 类型。(存储过程)

MyBatis-Plus 已经内置了一些常用的 TypeHandler,例如 JacksonTypeHandler 用于 JSON 类型的转换。如果没有满足需求的 TypeHandler,可以自定义 TypeHandler 来实现更复杂的类型转换逻辑。

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南

自定义 MyBatis-Plus TypeHandler 实战

场景:数据库字段 config 存储 JSON 格式的配置信息,Java 实体类 Userconfig 属性定义为 Map<String, Object> 类型。

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南

步骤

  1. 创建自定义 TypeHandler
import com.baomidou.mybatisplus.core.handlers.AbstractTypeHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

public class JsonMapTypeHandler extends AbstractTypeHandler<Map<String, Object>> {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
        try {
            ps.setString(i, objectMapper.writeValueAsString(parameter)); // 将 Map 转换为 JSON 字符串
        } catch (Exception e) {
            throw new SQLException(e);
        }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return parseJsonToMap(json); // 将 JSON 字符串转换为 Map
    }

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

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

    private Map<String, Object> parseJsonToMap(String json) {
        if (json == null || json.isEmpty()) {
            return null;
        }
        try {
            return objectMapper.readValue(json, Map.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 在实体类中指定 TypeHandler
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Map;

@Data
@TableName("user")
public class User {
    private Long id;
    private String name;

    @TableField(value = "config", typeHandler = JsonMapTypeHandler.class) // 使用自定义 TypeHandler
    private Map<String, Object> config;
}
  1. 在 MyBatis-Plus 配置中注册 TypeHandler(可选,如果使用全局类型处理器,则需要注册。也可以不注册,仅在 @TableField 中指定):
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // ... 其他拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> {
            configuration.getTypeHandlerRegistry().register(Map.class, JdbcType.VARCHAR, new JsonMapTypeHandler()); // 全局注册
        };
    }
}

实战避坑经验总结

  • 空指针异常:确保 TypeHandler 中处理 null 值的情况,避免空指针异常。可以使用 getNullableResult 方法处理。
  • JSON 解析异常:确保数据库中存储的 JSON 字符串格式正确,否则会导致 JSON 解析异常。可以使用 try-catch 块捕获异常并记录日志。
  • 性能问题:避免在 TypeHandler 中执行复杂的逻辑,影响性能。可以将复杂的逻辑放在业务层处理。
  • 字符集问题:确保数据库连接字符集与 Java 项目字符集一致,避免中文乱码问题。
  • 升级风险:MyBatis 和 MyBatis-Plus 版本升级时,TypeHandler 的 API 可能会发生变化,需要及时更新代码。

结合 Nginx、Redis 和 MySQL 的高并发场景考虑

在高并发场景下,例如使用 Nginx 作为反向代理服务器,通过负载均衡将请求分发到多个应用服务器,并使用 Redis 作为缓存,MySQL 存储持久化数据。此时,TypeHandler 的性能就显得尤为重要。需要尽量减少 TypeHandler 的执行时间,避免阻塞请求处理线程。例如,可以考虑使用更高效的 JSON 库,或者将 JSON 解析结果缓存到 Redis 中,减少数据库的访问压力。同时,也要注意 MySQL 的连接池配置,例如最大连接数、最小空闲连接数等,避免连接池耗尽导致服务不可用。可以使用 Druid 这样的数据库连接池监控工具,实时监控数据库连接池的状态。

对于使用了宝塔面板的用户,可以方便地通过宝塔面板管理 Nginx 和 MySQL,但也要注意宝塔面板默认的配置可能不是最优的,需要根据实际情况进行调整,例如调整 Nginx 的并发连接数、MySQL 的 buffer pool 大小等。

MyBatis-Plus 字段类型处理器深度解析与实战避坑指南

转载请注明出处: 脱发程序员

本文的链接地址: http://m.acea1.store/blog/871987.SHTML

本文最后 发布于2026-04-27 05:32:31,已经过了0天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 冬天里的一把火 7 小时前
    有没有关于 LocalDateTime 类型处理的 TypeHandler 例子?最近在做时间相关的项目。
  • 夏天的风 6 天前
    点赞!TypeHandler 这块确实容易被忽略,自定义 TypeHandler 的例子很实用。
  • 麻辣烫 3 天前
    点赞!TypeHandler 这块确实容易被忽略,自定义 TypeHandler 的例子很实用。