目录
前言
公司系统,mybatis-plus(v3.1.0)配置了MetaObjectHandler公共字段自动填充,一直以来都使用良好。昨天发现了个BUG——某些service方法更新时,不会自动填充edit_time,遂排查。
先说问题说在:XXService.update(Wrapper updateWrapper)方法。
MetaObjectHandler配置
public class MetaObjectHandlerConfig implements MetaObjectHandler {
// mybatis-plus公共字段自动填充,https://baomidou.oschina.io/mybatis-plus-doc/#/auto-fill
@Override
public void insertFill(MetaObject metaObject) {
// System.out.println("插入方法实体填充");
// System.out.println(DateUtils.toDateText(new Date(),DateUtils.DEFAULT_DATE_TIME_FORMAT));
//System.out.println("公共填充方法:" + metaObject.findProperty("creator", true));
//System.out.println(metaObject.getValue("creator"));
Date date = new Date();
setInsertFieldValByName("editor",metaObject.getValue("creator"),metaObject);
setInsertFieldValByName("createTime", date, metaObject);
setUpdateFieldValByName("editTime", date, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
// System.out.println("更新方法实体填充");
Date date = new Date();
setUpdateFieldValByName("editTime", date, metaObject);
}
}
插入时设置editor为creator,createTime,editTime为当前时间;更新时设置editTime字段值为当前时间。
entity字段注解
@Data
public class BaseEntity implements Serializable {
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
/**
* 修改时间
*/
@TableField(value = "edit_time", fill = FieldFill.INSERT_UPDATE)
private Date editTime;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private String creator;
/**
* 修改人
*/
private String editor;
/**
* 版本号
*/
@Version
private Long version;
/**
* 是否软删
*/
@TableLogic
private Boolean isDeleted;
}
editTime设置自动填充策略为:插入和更新填充字段
复现
@RunWith(SpringRunner.class)
@SpringBootTest
public class AutoFillTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoFillTest.class);
@Autowired
private UserMapper userMapper;
@Autowired
private IUserService userService;
@Test
public void test() throws InterruptedException {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId,1L);
wrapper.set(User::getAge,12);
//不会更新editTime
userService.update(wrapper);
//能更新
// userService.update(new User(),wrapper);
LOGGER.info("query user:{}",userMapper.selectById(1L));
}
}
userService.update(wrapper);不会更新edit_time;而用userService.update(new User(),wrapper);则能更新。很奇怪。
追踪源码到com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler#populateKeys方法 ->
代码165行发现:使用userService.update(wrapper);这种方法,tableInfo为null,没有拿到表相关字段信息,直接return,也就没有执行我们自定义的MetaObjectHandler配置。反之则能更新。
原因
看源码发现com.baomidou.mybatisplus.core.conditions.AbstractWrapper#entity,泛型T是 数据库表映射实体类,你不传则赋值null,并调用重载update方法(com.baomidou.mybatisplus.extension.service.IService#update(T, com.baomidou.mybatisplus.core.conditions.Wrapper<T>)) 。参考 ==>
如何解决
这就简单啦 -->
1.使用update的重载方法:userService.update(new User(),wrapper);
2.使用LambdaQueryWrapper<User> wrapper= new LambdaQueryWrapper<>(new User());
3.LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate(new User());
问题解决~
但有疑问
如上图136行:它实际上还是调用了update包含两个参数的重载方法,只不过entity赋值为null。回到128行代码,两个入参都是泛型T。
那么疑问来了:为什么136行代码entity参数不反射Wrapper的泛型参数T,实例化对象,再传入给entity参数呢?
......
......
结果是不行的,参考大佬博客怎样获取List的泛型参数,如List<String>? :
"看了一些别人的说法,说是带泛型参数的变量要么是成员变量,要么是方法参数才能获取泛型参数类型,因为java在类的方面只提供了一个getGenericSuperClass()的方法,只能获取到父类的泛型参数类型。在成员变量和方法上还有getGenericType()和getGenericParameterTypes()方法。这是泛型的类型擦除导致的,具体我还没有探究。
但这不是纯粹的泛型,因为要明确给出类型才能获取。如定义方法getName(List<String> lists),使用反射可以很容易得到String类型,但如果把方法改为getName(List <T> lists),无论你使用什么方法都不可能得到运行时给出的T的类型,只能得到字符串“T”。有人说有个什么框架(忘了)采用了这样一个方法:定义一个内部类,把T传给该类的成员变量,然后使用匿名内部类得到该成员变量,我试了,绕了一大圈,最后得到的还是“T”,要想得到运行时传递的泛型参数类型,就必须在方法中明确声明泛型类型。
那么如果真的想使用纯粹的泛型参数(确实有这个需求,比如设计一些工具方法时)怎么办呢,其实有一种方法,不需要反射(如果用反射,代码还要多几行),就是加一个Class的参数,如getName(Class clazz, List<T> lists),这样调用方法时给出clazz即可,而clazz就是T的Class。如果你想说这代码不够优雅,那我也没办法了。"
好吧,看来泛型和反射方面的知识我还有待补充啊。
完~
来源:CSDN
作者:Canon in D
链接:https://blog.csdn.net/weixin_42683408/article/details/103586466