欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

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

2025年08月05日 Java
1 现象mysql 使用json类型字段 存储list<string>,java类属性使用typehandler 没有生效。代码如下实体类import com.baomidou.mybat

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不生效内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!