MyBatis-Plus中JSON字段处理的常见问题与解决方案

发布于:2025-08-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

在我们的项目中,有一个业务实体类包含一个JSON类型的字段,该字段存储的是一个对象列表。为了处理这个JSON字段,我们自定义了一个类型处理器(TypeHandler)来实现对象与JSON字符串之间的转换。

实体类定义大致如下:

public class BusinessModel {
    private Long id;
    
    // 其他普通字段...
    
    /**
     * JSON字段,存储关系列表
     */
    @TableField(typeHandler = CustomJsonTypeHandler.class)
    private List<RelationObject> relations;
    
    // getter/setter方法...
}

在实际使用中,我们发现了一个奇怪的现象:

  • 使用自定义Mapper方法保存对象时,JSON字段能正确处理

  • 使用MyBatis-Plus的内置save方法保存对象时,JSON字段处理失败

// 方式1:使用自定义Mapper方法 - 正常工作
this.baseMapper.insertOrUpdate(data);

// 方式2:使用MyBatis-Plus内置方法 - JSON处理失败
this.save(data);

问题分析

经过深入分析,我们发现问题的根本原因在于MyBatis-Plus的自动SQL生成机制与自定义类型处理器的兼容性问题。

1. MyBatis-Plus的save方法工作机制

MyBatis-Plus的内置方法(如save、update等)会自动生成SQL语句,而不是使用我们在XML中定义的SQL映射。这种方式虽然简化了开发,但也带来了一些限制:

  • 自动生成的SQL不会包含我们在XML中定义的typeHandler

  • 对于自定义类型(如JSON字段),MyBatis-Plus无法正确识别和处理

2. 自定义Mapper方法的优势

当我们使用自定义Mapper方法时,MyBatis会严格按照XML中定义的SQL和映射规则执行操作,包括使用指定的typeHandler处理JSON字段。

在我们的XML映射文件中,明确指定了类型处理器:

<resultMap id="BaseResultMap" type="BusinessModel">
    <id property="id" column="id" />
    <!-- 其他字段映射... -->
    <result property="relations" column="relations" typeHandler="CustomJsonTypeHandler"/>
</resultMap>

<insert id="insertOrUpdate">
    INSERT INTO business_table (id, relations, ...)
    VALUES (#{id}, #{relations, typeHandler=CustomJsonTypeHandler}, ...)
    ON CONFLICT (id) DO UPDATE SET
    relations = #{relations, typeHandler=CustomJsonTypeHandler}
</insert>

解决方案

为了让MyBatis-Plus的内置方法也能正确处理JSON字段,我们需要进行以下配置:

1. 配置MyBatis-Plus扫描类型处理器

在application.yml中添加类型处理器包的扫描配置:

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: AUTO
  # 添加类型处理器包路径,让MyBatis-Plus自动扫描和注册
  type-handlers-package: com.yourpackage.typehandler

2. 确保类型处理器正确实现

类型处理器需要正确实现MyBatis的BaseTypeHandler,并确保有无参构造函数:

@MappedJdbcTypes(JdbcType.OTHER)
@MappedTypes({List.class})
public abstract class BaseJsonTypeHandler<T> extends BaseTypeHandler<List<T>> {
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException {
        String content = CollectionUtils.isEmpty(parameter) ? null : JSON.toJSONString(parameter);
        ps.setObject(i, content, java.sql.Types.OTHER);
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.parseJson(rs.getString(columnName));
    }

    @Override
    public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.parseJson(rs.getString(columnIndex));
    }

    @Override
    public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.parseJson(cs.getString(columnIndex));
    }

    private List<T> parseJson(String content) {
        if (StringUtils.isEmpty(content)) {
            return new ArrayList<>();
        }
        return JSON.parseObject(content, this.getSpecificType());
    }

    /**
     * 获取具体类型
     */
    protected abstract TypeReference<List<T>> getSpecificType();
    
    // 添加无参构造函数以支持MyBatis-Plus的自动实例化
    public BaseJsonTypeHandler() {
    }
}

3. 实体类正确使用注解

在实体类中正确使用@TableField注解指定typeHandler:

public class BusinessModel {
    private Long id;
    
    /**
     * JSON字段,存储关系列表
     */
    @TableField(typeHandler = CustomJsonTypeHandler.class)
    private List<RelationObject> relations;
    
    // getter/setter方法...
}

总结

通过以上配置,我们就可以正常使用MyBatis-Plus的内置方法来处理包含JSON字段的实体对象了。这个问题的核心在于MyBatis-Plus的自动SQL生成机制与自定义类型处理器之间的兼容性问题。

关键要点:

  1. 在application.yml中配置type-handlers-package让MyBatis-Plus能够自动扫描和注册类型处理器

  2. 确保类型处理器类有无参构造函数以支持自动实例化

  3. 实体类中正确使用@TableField注解指定typeHandler

这样配置后,MyBatis-Plus的save方法就能正确处理JSON字段的序列化和反序列化,与自定义Mapper方法达到同样的效果。

在实际项目开发中,遇到这类问题时,我们应该首先检查MyBatis-Plus的配置是否完整,确保自定义组件能够被正确识别和使用。


网站公告

今日签到

点亮在社区的每一天
去签到