MyBatis-Plus TypeHander不生效的问题解决
作者:Always_July
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不生效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!