当前使用版本(必须填写清楚,否则不予处理)
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):
插入时的日志:
[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都不生效
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);
运行日志:
如果对应的
@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.