当前使用版本(必须填写清楚,否则不予处理)

mybatis-pluse 3.0.7.1 mysql5.7

该问题是怎么引起的?(最新版上已修复的会直接close掉)

实体类中有一个类的字段属性为List或者Map,对应的数据库字段为JSON,插入时,List和Map有通过自定义的TypeHandler转为JSON字符串,但是更新时没有通过自定义的TypeHandler进行转换

abstract class JsonTypeHandler<T>(val type: Class<T>): BaseTypeHandler<T>() {

    private val objectMapper = ObjectMapper()

    override fun getNullableResult(rs: ResultSet, columnName: String?): T? {
        return parse(rs.getString(columnName?: ""))
    }


    override fun getNullableResult(rs: ResultSet, columnIndex: Int): T? {
        return parse(rs.getString(columnIndex))
    }

    override fun getNullableResult(cs: CallableStatement, columnIndex: Int): T? {
        return parse(cs.getString(columnIndex))
    }

    override fun setNonNullParameter(ps: PreparedStatement, i: Int, parameter: T, jdbcType: JdbcType?) {
        ps.setString(i, objectMapper.writeValueAsString(parameter))
    }

    private fun parse(s: String?): T? {
        return if (StringUtils.isBlank(s)) {
            null
        } else {
            objectMapper.readValue(s, type)
        }
    }

}
class ListTypeHandler: JsonTypeHandler<List<*>>(List::class.java)

class MapTypeHandler: JsonTypeHandler<Map<*, *>>(Map::class.java)

更新时的日志(picAlbums为JSON): MyBatis-Plus 更新时自定义的TypeHandler不生效 插入时的日志:

[nio-8080-exec-2] c.h.s.h.s.p.m.P.insert                   : ==>  Preparing: INSERT INTO product_templates ( id, merchantId, categoryId, systemCode, itemType, title, subtitle, picAlbums, unitName, unitQuality, specification, price, description ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) 
2019-01-14 09:41:28.794 DEBUG 3224 --- [nio-8080-exec-2] c.h.s.h.s.p.m.P.insert                   : ==> Parameters: 1084626501050572802(String), xxxxxx(String), 1083971735584858114(String), ca412ed1-7a5a-45f1-8dcb-8e955c355cba(String), 1(Integer), 狼途(String), 狼途V5(String), [](String), 箱(String), 500(Integer), {"颜色":["红色","绿色"],"大小":["200","400"],"尺寸":["21","30"]}(String), 25(Long), hahaha(String)

重现步骤

报错信息

Comment From: miemieYaho

用的哪个 update 方法?怎么用的?

Comment From: jervainy

IService的updateById,和update通过UpdateWrapper操作,TypeHandler都不生效 MyBatis-Plus 更新时自定义的TypeHandler不生效

Comment From: miemieYaho

UpdateWrapper 不支持这个

Comment From: jervainy

updateById呢

Comment From: jervainy

自定义的TypeHandler通过typeHandlerPackages注入进去后,只有在insert时生效,在update时不生效,我试过update的很多形式,都不生效,比如updateById(entity T), update(entity T, wrapper Wrapper) , update(uw, UpdateWrapper)

Comment From: miemieYaho

你没用我们提供的注解指定 typehandler 吧.那这个就是原生 mybatis 的事了,和我们没有关系

Comment From: jervainy

没有发现注解有typehandler的,应该是3.0.7.1移除了吧,另外上一个版本我用的原生Mybatis,是可以的,这次进行版本重构,我改为的mybatis plus,原生mybatis我在mybatis.xml中注入了typehandler然后是可以的,mybatis plus我也有通过mybatis.xml来注入,没有使用typeHandlerPackages,也不行

Comment From: miemieYaho

怎么注入的? https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableField.java
你可以写一个复现 demo

Comment From: jervainy

class ProductTemplatesEntity {
    var picAlbums: List<String>? = null
    var specification: Map<String, Set<Any>>? = null
}

Comment From: jervainy

mybatis-plus:
  global-config:
    db-config:
      logic-not-delete-value: 0
      logic-delete-value: 1
      id-type: id_worker_str
  type-aliases-package: com.hipmarket.service.hip.serivce.product.data
  mapper-locations: classpath:mapper/*.xml
  type-handlers-package: com.hipmarket.service.hip.service.product.typehandler
#  config-location: classpath:mybatis.xml

<configuration>

    <typeHandlers>
        <typeHandler handler="com.hipmarket.service.hip.service.product.typehandler.ListTypeHandler" />
        <typeHandler handler="com.hipmarket.service.hip.service.product.typehandler.MapTypeHandler" />
    </typeHandlers>

</configuration>

Comment From: miemieYaho

要么你自己 debug, 要么给出 demo 而不是你的配置

Comment From: jervainy

我刚刚新建了一个项目,写了一个精简的Demo,怎么给你呢

Comment From: miemieYaho

git

Comment From: jervainy

demo.zip 直接运行测试用例

Comment From: miemieYaho

git 不要 zip

Comment From: jervainy

https://gitee.com/jervain_y/mybatis-plus-demo01.git

Comment From: Cat7373

@jervainy https://gitee.com/jervain_y/mybatis-plus-demo01/blob/master/src/main/resources/application.yml 你数据库的 root 密码暴露了😂

Comment From: jervainy

@Cat7373 没关系,不是线上的

Comment From: miemieYaho

测试过了,我们注入的 sql 是需要和注解配合使用的,也就是 @param("et"),而你自己写的 sql 是不用注解的,如果你改成注解式的比如@param("a")然后在 sql 里进行 a.demo取值,一样是获取不到 typehandler 的

Comment From: jervainy

你的意思是通过UpdateWrapper来更新数据实际上是在自己生成SQL吗?如果是的话那与我自己通过@Select写SQL有什么区别呢

Comment From: miemieYaho

我的意思是,一旦用了 @param 注解,并在 sql 内用 注解值.字段名获取的数据,都不会自动获取 typehandler,懂?

Comment From: jervainy

我没有使用@Param注解,update的实现是通过这个方式来的吗

Comment From: miemieYaho

Comment From: jervainy

原因清楚了,也测试过,原生mybatis同样存在这个问题,建议mybatis-plus后续@TableField能否加上typeHandler属性,如下:

@Update("update a set demo = #{a.demo,typeHandler=com.example.demo.typehandler.ListTypeHandler} where a.id = #{a.id}")
void updateMyOne(@Param("a") A a);

运行日志: MyBatis-Plus 更新时自定义的TypeHandler不生效 如果对应的@TableField存在typeHandler则在SQL中可以指定typeHandler 我把我刚刚测试的Demo更新到git上了,谢谢解答

Comment From: miemieYaho

el属性里有

Comment From: jervainy

可以参照#270

Comment From: makejavas

@jervainy 尝试更新到3.0.8.3-SNAPSHOT解决。

Comment From: jervainy

@makejavas 仓库地址多少,3.0.8.3-SNAPSHOT更新不下来

Comment From: makejavas

https://oss.sonatype.org/content/repositories/snapshots/

Comment From: jervainy

@makejavas 3.0.8.3-SNAPSHOT ok,期待release版本

Comment From: miemieYaho

后续版本不再如此支持,你这种 typehandler 的泛型的 class 和 set 的值的 class 不是同一个时,还是必须使用 el 属性

Comment From: ToryZhou

我也是遇到调用updateById方法报错

Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'

加上注解解决的

    @TableField(el = "languageMap, typeHandler=com.xxx.entity.rds.type.MapTypeHandler")
    private Map<String, String> languageMap;

Comment From: xgj1988

这个问题 在3.1.0下面还是有。。就不能兼容一下吗?在不写 el typeHandler的情况下也支持一下。

Comment From: makejavas

大佬说处理这个问题会影响性能,就干掉了。老老实实手动写el吧

Comment From: xgj1988

好,,,大佬说了算。

Comment From: muzhongjiang

@TableField(typeHandler = CollectionTypeHandler.class)
private Collection<Integer> dataSetIds;

Comment From: jervainy

typeHandler是3.1.2加入的,还有一种方式,自定义TypeHandler上加上MappedTypes注解,指明处理的类型,需要指定确切的类型

Comment From: miracle-bean

LambdaQueryWrapper 是不是同样也不支持自定义typeHandle?有没有什么解决办法

Comment From: ninjachen

我在3.4.3.3用updateById()可以更新typeHandler的字段, 但LambdaUpdateWrapper还是不生效,上文提到的el annotation参数也不见了。。。

        // works
        PluginRelease pluginRelease = findOrFail(id);
        pluginRelease.setLastPublishTime(LocalDateTime.now());
        updateById(pluginRelease)
        // not works
        LambdaUpdateWrapper<PluginRelease> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(PluginRelease::getId, id)
                .set(PluginRelease::getLastPublishTime, LocalDateTime.now());
        getBaseMapper().update(null, updateWrapper);

Comment From: gm100861

我在3.4.3.3用updateById()可以更新typeHandler的字段, 但LambdaUpdateWrapper还是不生效,上文提到的el annotation参数也不见了。。。

// works PluginRelease pluginRelease = findOrFail(id); pluginRelease.setLastPublishTime(LocalDateTime.now()); updateById(pluginRelease)

// not works LambdaUpdateWrapper<PluginRelease> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(PluginRelease::getId, id) .set(PluginRelease::getLastPublishTime, LocalDateTime.now()); getBaseMapper().update(null, updateWrapper);

我也遇到了相同的问题,3.5.2版本

Comment From: pakchoily

我在3.4.3.3用updateById()可以更新typeHandler的字段, 但LambdaUpdateWrapper还是不生效,上文提到的el annotation参数也不见了。。。

// works PluginRelease pluginRelease = findOrFail(id); pluginRelease.setLastPublishTime(LocalDateTime.now()); updateById(pluginRelease)

// not works LambdaUpdateWrapper<PluginRelease> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(PluginRelease::getId, id) .set(PluginRelease::getLastPublishTime, LocalDateTime.now()); getBaseMapper().update(null, updateWrapper);

能这样解决我已经心满意足了,懒得自己写。

Comment From: jk2K

relate to https://github.com/baomidou/mybatis-plus/issues/3183

https://baomidou.com/guides/type-handler/#wrapper-%E6%9F%A5%E8%AF%A2%E4%B8%AD%E7%9A%84-typehandler-%E4%BD%BF%E7%94%A8

从 MyBatis-Plus 3.5.3.2 版本开始,可以在 Wrapper 查询中直接使用 TypeHandler

LambdaUpdateWrapper<WorkflowDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(WorkflowDO::getActionsUsed, actionUsedDTOS, "typeHandler=" + ActionUsedJsonTypeHandler.class.getCanonicalName());
updateWrapper.eq(WorkflowDO::getWorkflowId, 1020015);
workflowService.update(updateWrapper);

Comment From: fatcarter

relate to #3183

https://baomidou.com/guides/type-handler/#wrapper-%E6%9F%A5%E8%AF%A2%E4%B8%AD%E7%9A%84-typehandler-%E4%BD%BF%E7%94%A8

从 MyBatis-Plus 3.5.3.2 版本开始,可以在 Wrapper 查询中直接使用 TypeHandler

java LambdaUpdateWrapper<WorkflowDO> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(WorkflowDO::getActionsUsed, actionUsedDTOS, "typeHandler=" + ActionUsedJsonTypeHandler.class.getCanonicalName()); updateWrapper.eq(WorkflowDO::getWorkflowId, 1020015); workflowService.update(updateWrapper);

@TableField注解中包含了typeHandler字段,为什么这里需要重复添加typeHandler, 不可以从注解中读取吗? 看起来好像有点累赘

Comment From: Panlq

可以用 setSql 替代 setSql("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串");

setSql("json_field_name = {0,javaType=json对象对应的代码里定义的类型,jdbcType=CLOB,typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"}, 赋值的json内容字段)

或者直接 json str setSql("json_field_name = {0}", JsonHelper.toJSONString(xxxxx))

setSql javaType 无法正常解析 内嵌形式的 class, 会报错 @miemieYaho

Could not resolve type alias  xxx.parentClass.subStaticClass. Cause: java.lang.ClassNotFoundException: Cannot find class xxx.parentClass.subStaticClass.