公司持久层框架使用的是Mybatis增强版:Mybatis-Plus,再一次做更新操作时,报了一个错误,由于隔得时间太久,代码也已经进行过改动,所以只有采用其它方式重现一下。
问题再现
首先新建一个表,很简单没什么说的:
新建一个实体类:
package com.aecc.smart.fire.server.entity;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("em_test")
public class EmTest implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
@TableField("name")
private String name;
@TableField("age")
private int age;
}
然后创建空的service、serviceImpl、mapper:
public interface EmTestService extends IService<EmTest> {
}
@Service
public class EmTestServiceImpl extends ServiceImpl<EmTestMapper, EmTest> implements EmTestService {
}
public interface EmTestMapper extends BaseMapper<EmTest> {
}
新建测试类:
package com.aecc.smart.fire.server;
import com.aecc.smart.fire.server.entity.EmTest;
import com.aecc.smart.fire.server.service.EmTestService;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class EmTest1 {
@Autowired
private EmTestService emTestService;
@Test
public void insertOrUpdate(){
String emTestString = "{\n" +
// " \"id\": \"1278618408770588673\",\n" +
" \"name\": \"lisi\",\n" +
" \"age\": 18\n" +
"}";
EmTest emTest = JSON.parseObject(emTestString, EmTest.class);
emTestService.insertOrUpdate(emTest);
System.out.println("...");
}
}
执行测试,结果正常,关键输出信息如下:
表中多了一条记录:
再执行更新操作:
package com.aecc.smart.fire.server;
import com.aecc.smart.fire.server.entity.EmTest;
import com.aecc.smart.fire.server.service.EmTestService;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class EmTest1 {
@Autowired
private EmTestService emTestService;
@Test
public void insertOrUpdate(){
String emTestString = "{\n" +
" \"id\": \"1278623237685329921\",\n" +
" \"name\": \"lisi\",\n" +
" \"age\": 18\n" +
"}";
EmTest emTest = JSON.parseObject(emTestString, EmTest.class);
emTestService.insertOrUpdate(emTest);
System.out.println("...");
}
}
报错了:
因为项目里面添加了自动填充处理器:
package com.aecc.smart.fire.server.config;
import java.util.Date;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import com.aecc.smart.fire.server.feign.BaseContextUtils;
import com.baomidou.mybatisplus.mapper.MetaObjectHandler;
/**
* 自动填充处理器
* @Description TODO
* @date 2019年11月14日 下午1:41:10
*/
@Component
public class MyMetaObjectHandler extends MetaObjectHandler{
@Override
public void insertFill(MetaObject metaObject) {
// TODO Auto-generated method stub
this.setFieldValByName("addTime", new Date(), metaObject);
this.setFieldValByName("addPerson", BaseContextUtils.getUserID(), metaObject);
this.setFieldValByName("deleted", 0, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
// TODO Auto-generated method stub
this.setFieldValByName("lastModifyTime", new Date(), metaObject);
this.setFieldValByName("lastModifyPerson", BaseContextUtils.getUserID(), metaObject);
}
}
但是添加的时候,实体类没有insertFill方法中执行setFieldValByName方法时不会报错。
分析如下:
首先看BaseMapper:
updateById方法执行的时候,把entity中字段的值处理成"et_"+字段名来最后填充占位符的值。
MetaObjectHandler中方法:
可知,添加的时候在处理不存在字段addTime等时,两个if条件,都不满足,返回this;而更新的时候处理不存在字段lastModifyTime时,会进入到else if判断条件中。原因往下看:
在执行MetaObjectHandler的setFieldValByName方法前,MetaObject对象已经处理过,而MetaObject对象中的setValue其实就是给这些字段设置值,并且有一个(“et_”->object对象)的属性->值。
最后跟踪到如下方法:
从HashMap类型的setMethods属性中找propertyName为lastModify就会找不到,进而抛出异常。
解决方式有两个:
- 在实体类添加公共字段,数据库表也添加对应字段(不推荐,因为表中本来就不需要这些字段)
- 修改的时候,手动写sql