实体类City的主键是Integer类型的,在进行insert操作时,MyBatis Plus自动生成了一个Long类型的主键id,导致参数类型不匹配,出现上述错误
经过查看日志和调试发现,MyBatis最终调用BeanWrapper的setBeanProperty方法,通过反射执行最终的插入操作(增删改查应该都是通过此处的反射,不过暂时只调试了insert方法)
此处传入的object即为我们想要保存到数据库的实体信息(不带ID信息),value为主键信息,此时主键值已经是一个Long类型的值,我们接着向上看value是哪里传过来的
setBeanProperty是一个私有方法,在本类调用,查询看到set(PropertyTokenizer prop, Object value)方法调用了它
set方法中的value也是其他地方传入的
查看报错日志可以看到,BeanWrapper的上一层是MetaObject,我们找到MetaObject,看到在getValue方法中调用了BeanWrapper的set方法(BeanWrapper实现了ObjectWrapper)
MetaObject中的主键值也是上层调用传入的,继续根据错误日志向上看:MybatisDefaultParameterHandler
在populateKeys方法中调用了metaObject.setValue()
可以看到,此处根据IdType生成不同类型的主键id,IdType是一个枚举类,定义了生成ID的类型
AUTO 数据库ID自增INPUT 用户输入IDID_WORKER 全局唯一ID,Long类型的主键ID_WORKER_STR 字符串全局唯一IDUUID 全局唯一ID,UUID类型的主键NONE 该类型为未设置主键类型当IdType的类型为ID_WORKER、ID_WORKER_STR或者UUID时,主键由MyBatis Plus的IdWorker类生成
ID_WORKER调用IdWorker的getId()方法,生成一个与时间相关的主键id
IdWorker的getId()方法引用了Sequence的nextId()方法
ID_WORKER_STR将worker.nextId()的返回值转化为字符串,和getId()方法相似
UUID去除中划线的UUID字符串
此时,已经基本可以确认,问题出在IdType的配置上,那么这个IdType从哪里获取的呢?TableInfo中获取的!
TableInfo是数据库表反射信息实体类,此处由其他方法传入的,查看日志:
通过日志可以看出,populateKeys()方法是在processBatch()方法中调用的,找到该方法
下面对TableInfo生成的这段代码做个说明
parameterObject:要保存到数据库中的实体类信息
我在保存数据时,参数形式并不是Map类型的,所以直接跳转到else中
根据保存的实体类的类型去获取数据库表反射信息
我们看下getTableInfo()方法的实现
从tableInfoCache中获取指定类型的数据库反射信息。tableInfoCache是一个线程安全的私有静态Map,主要用于存放类型和数据库表的映射关系。断点调试到此处,看下tableInfoCache的内容
可以看到,idType的值为ID_WORKER,即生成一个与时间相关的Long类型的id。在放入到tableInfoCache的时候就已经指定了idType的值。
查看TableInfoHelper的源码可以得知,initTableInfo()方法负责initTableInfo的初始化
在existTableId方法中判断主键注解@TableId是否存在
当不存在主键注解时,会调用initFieldId()方法对主键属性进行初始化
至此, 我们基本可以判断是因为在实体类City中id属性没有加@TableId注解,我们看下TableId的源码
TableId的类型通过type来指定,默认是IdType.NONE(该类型为未设置主键类型),City表是通过数据库的自增序列实现的,所以设置为AUTO
然后测试,程序正常运行,保存数据成功。
扩展对于tableInfo默认的idType值配置,可以看出用的是全局配置的idType,全局配置的值是在initTableInfo()方法中获取的,有兴趣的话可以去看看全局配置的实现,此处暂不深入了
到此这篇关于浅谈MyBatis Plus主键设置策略的文章就介绍到这了,更多相关MyBatis Plus主键设置内容请搜索七叶笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持七叶笔记!