MyBatis-Plus TypeHander不生效的问题解决

 更新时间:2025年08月05日 11:35:05   作者:Always_July  
MyBatis-Plus中使用lambdaUpdate更新JSON字段时,TypeHandler未生效导致数据截断错误,需手动转换或显式指定typeHandler解决,下面就来介绍一下该问题的解决

1 现象

mysql 使用json类型字段 存储List<String>,java类属性使用TypeHandler 没有生效。

代码如下

实体类

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
public class MessageDO {
        @TableField(typeHandler = FastjsonTypeHandler.class)
        private List<String> notifyType;
   
    }

service 层

@Override
public void messageUpdate(MessageUpdateReqDTO reqDTO) {
    lambdaUpdate()
            .set(PartnerDO::getWxNotifyType,reqDTO.getWxNotifyType())
            .eq(PartnerDO::getCompanyId,reqDTO.getCompanyId())
            .update();
}

报错如下

关键信息:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.

 Cause: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
; Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.; nested exception is com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
	at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
	at com.sun.proxy.$Proxy146.update(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:287)
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:64)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
	at com.sun.proxy.$Proxy238.update(Unknown Source)
	at com.baomidou.mybatisplus.extension.conditions.update.ChainUpdate.update(ChainUpdate.java:45)
	at com.baomidou.mybatisplus.extension.conditions.update.ChainUpdate.update(ChainUpdate.java:35)

令人疑惑,我明明配置TypeHandler,但是没有生效,在 com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler#toJson 的方法打了断点,还是没有进入断点。

2.源码分析

TypeHandler的源码

com.baomidou.mybatisplus.core.MybatisParameterHandler#setParameters

public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (this.boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = this.boundSql.getAdditionalParameter(propertyName);
                } else if (this.parameterObject == null) {
                    value = null;
                } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = this.configuration.getJdbcTypeForNull();
                }
                try {
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

Debug了一下,发现根本没有解析出TypeHandler

ParameterMapping 解析

org.apache.ibatis.builder.SqlSourceBuilder#parse

public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
  this.sql = sql;
  this.parameterMappings = parameterMappings;
  this.configuration = configuration;
}

@Override
public BoundSql getBoundSql(Object parameterObject) {
  return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}

org.apache.ibatis.builder.SqlSourceBuilder#parse

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql = parser.parse(originalSql);
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

org.apache.ibatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#buildParameterMapping

private ParameterMapping buildParameterMapping(String content) {
  Map<String, String> propertiesMap = parseParameterMapping(content);
  String property = propertiesMap.get("property");
  Class<?> propertyType;
  if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
    propertyType = metaParameters.getGetterType(property);
  } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
    propertyType = parameterType;
  } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
    propertyType = java.sql.ResultSet.class;
  } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
    propertyType = Object.class;
  } else {
    MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
    if (metaClass.hasGetter(property)) {
      propertyType = metaClass.getGetterType(property);
    } else {
      propertyType = Object.class;
    }
  }
  ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
  Class<?> javaType = propertyType;
  String typeHandlerAlias = null;
  for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
    String name = entry.getKey();
    String value = entry.getValue();
    if ("javaType".equals(name)) {
      javaType = resolveClass(value);
      builder.javaType(javaType);
    } else if ("jdbcType".equals(name)) {
      builder.jdbcType(resolveJdbcType(value));
    } else if ("mode".equals(name)) {
      builder.mode(resolveParameterMode(value));
    } else if ("numericScale".equals(name)) {
      builder.numericScale(Integer.valueOf(value));
    } else if ("resultMap".equals(name)) {
      builder.resultMapId(value);
    } else if ("typeHandler".equals(name)) {
      typeHandlerAlias = value;
    } else if ("jdbcTypeName".equals(name)) {
      builder.jdbcTypeName(value);
    } else if ("property".equals(name)) {
      // Do Nothing
    } else if ("expression".equals(name)) {
      throw new BuilderException("Expression based parameters are not supported yet");
    } else {
      throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + PARAMETER_PROPERTIES);
    }
  }
  if (typeHandlerAlias != null) {
    builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
  }
  return builder.build();
}

lambdaUpdate set源码

查看API,发现可以传入mapping参数,指定typeHandler。

com.baomidou.mybatisplus.core.conditions.update.Update#set(R, java.lang.Object)

default Children set(R column, Object val) {
    return set(true, column, val);
}
default Children set(boolean condition, R column, Object val) {
    return set(condition, column, val, null);
}
/**
 * 设置 更新 SQL 的 SET 片段
 *
 * @param condition 是否加入 set
 * @param column    字段
 * @param val       值
 * @param mapping   例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler
 * @return children
 */
Children set(boolean condition, R column, Object val, String mapping);

3.解决方法

1 手动转为字符串

lambdaUpdate()
        .set(PartnerDO::getWxNotifyType,JSON.toJSONString(reqDTO.getWxNotifyType()))
        .eq(PartnerDO::getCompanyId,reqDTO.getCompanyId()) .update();

2 添加TypeHandler

lambdaUpdate()
.set(PartnerDO::getWxNotifyType,reqDTO.getWxNotifyType(),"typeHandler=com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler")
        .eq(PartnerDO::getCompanyId,reqDTO.getCompanyId())
        .update();

4.环境

mybatis-plus

<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3.4</version>
</dependency>

5.总结

MyBatis-Plus 使用 lambdaUpdate更新某个属性,该属性的typehandler无效,需要手动转化或传入typeHandler。

到此这篇关于MyBatis-Plus TypeHander不生效的问题解决的文章就介绍到这了,更多相关MyBatis-Plus TypeHander不生效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用JPA中@Query 注解实现update 操作方法(必看)

    使用JPA中@Query 注解实现update 操作方法(必看)

    下面小编就为大家带来一篇使用JPA中@Query 注解实现update 操作方法(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • SpringBoot基于Mybatis拦截器和JSqlParser实现数据隔离

    SpringBoot基于Mybatis拦截器和JSqlParser实现数据隔离

    本文将介绍如何在 Spring Boot 项目中利用Mybatis的强大拦截器机制结合JSqlParser,一个功能丰富的 SQL 解析器,来轻松实现数据隔离的目标,本文根据示例展示如何根据当前的运行环境来实现数据隔离,需要的朋友可以参考下
    2024-04-04
  • 使用lombok@Data存在extends时需要注意的问题

    使用lombok@Data存在extends时需要注意的问题

    在Java编程中,正确实现equals方法是保证对象比较一致性的关键,使用instanceof检查类型可能导致违反对称性原则,即当子类和父类都重写equals时可能出现a.equals(b)不等于b.equals(a)的情况,Lombok的@EqualsAndHashCode注解可以通过callSuper=true参数
    2024-10-10
  • Idea如何使用System.getenv()获取环境变量的值

    Idea如何使用System.getenv()获取环境变量的值

    文章介绍了如何在Java中使用`System.getenv()`方法读取环境变量的值,并提供了两种配置环境变量的方法:启动项配置和系统环境变量配置,对于系统环境变量,文章特别指出需要重启电脑或程序才能使其生效
    2024-11-11
  • 深入解析面向对象编程在Java中的应用小结

    深入解析面向对象编程在Java中的应用小结

    本文详细介绍了面向对象编程的基本概念,包括类和对象、封装、继承和多态,通过具体的Java代码示例,展示了如何在Java中应用这些面向对象编程的核心思想,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • java SpringMVC学习使用详解

    java SpringMVC学习使用详解

    本篇文章主要介绍了java SpringMVC——如何获取请求参数详解,详细的介绍了每种参数注解的用法及其实例。感兴趣的小伙伴们可以参考一下
    2017-04-04
  • SpringBoot请求响应方式示例详解

    SpringBoot请求响应方式示例详解

    这篇文章主要介绍了SpringBoot请求响应的相关操作,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • SpringBoot整合MongoDB的示例

    SpringBoot整合MongoDB的示例

    这篇文章主要介绍了SpringBoot整合MongoDB的示例,帮助大家更好的理解和学习springboot框架,感兴趣的朋友可以了解下
    2020-10-10
  • Mybatis传递List集合方式

    Mybatis传递List集合方式

    MyBatis传递List参数时,若XML变量名不匹配默认的"list"键,会报错,需通过@Param指定名称、使用collection属性或索引方式正确引用
    2025-07-07
  • 详解SpringBoot如何优雅的进行全局异常处理

    详解SpringBoot如何优雅的进行全局异常处理

    在SpringBoot的开发中,为了提高程序运行的鲁棒性,我们经常需要对各种程序异常进行处理,但是如果在每个出异常的地方进行单独处理的话,这会引入大量业务不相关的异常处理代码,这篇文章带大家了解一下如何优雅的进行全局异常处理
    2023-07-07

最新评论