温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

MyBatis Plus主键设置策略是什么

发布时间:2022-07-08 13:49:02 来源:亿速云 阅读:289 作者:iii 栏目:开发技术

这篇文章主要介绍了MyBatis Plus主键设置策略是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MyBatis Plus主键设置策略是什么文章都会有所收获,下面我们一起来看看吧。

根据一次插入失败报错来了解下MyBatis Plus主键设置策略
今天学习使用MyBatis Plus,发现使用代码生成器生成对应的实体类、Service和Mapper后,在保存数据时报错

com.baomidou.mybatisplus.exceptions.MybatisPlusException: java.lang.reflect.InvocationTargetException     at com.baomidou.mybatisplus.MybatisSqlSessionTemplate$SqlSessionInterceptor.invoke(MybatisSqlSessionTemplate.java:405)     at com.sun.proxy.$Proxy70.insert(Unknown Source)     at com.baomidou.mybatisplus.MybatisSqlSessionTemplate.insert(MybatisSqlSessionTemplate.java:243)     at com.baomidou.mybatisplus.activerecord.Model.insert(Model.java:56)     at com.lemon.rabbit.common.base.city.CityInitial.printInfo(CityInitial.java:112)     at com.lemon.rabbit.common.base.city.CityInitial.parseNextLevel(CityInitial.java:87)     at com.lemon.rabbit.common.base.city.CityInitial.test(CityInitial.java:59)     at com.lemon.rabbit.RabbitApplicationTests.contextLoads(RabbitApplicationTests.java:19)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:498)     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)     at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)     at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)     at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)     at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)     at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)     at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)     at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: java.lang.reflect.InvocationTargetException     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:498)     at com.baomidou.mybatisplus.MybatisSqlSessionTemplate$SqlSessionInterceptor.invoke(MybatisSqlSessionTemplate.java:401)     ... 37 more Caused by: org.apache.ibatis.exceptions.PersistenceException:  ### Error updating database.  Cause: org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.lemon.rabbit.model.City' with value '1017367047558582273' Cause: java.lang.IllegalArgumentException: argument type mismatch ### Cause: org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.lemon.rabbit.model.City' with value '1017367047558582273' Cause: java.lang.IllegalArgumentException: argument type mismatch     at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)     at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200)     at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)     ... 42 more Caused by: org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.lemon.rabbit.model.City' with value '1017367047558582273' Cause: java.lang.IllegalArgumentException: argument type mismatch     at org.apache.ibatis.reflection.wrapper.BeanWrapper.setBeanProperty(BeanWrapper.java:185)     at org.apache.ibatis.reflection.wrapper.BeanWrapper.set(BeanWrapper.java:59)     at org.apache.ibatis.reflection.MetaObject.setValue(MetaObject.java:140)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.populateKeys(MybatisDefaultParameterHandler.java:217)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.processBatch(MybatisDefaultParameterHandler.java:156)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.<init>(MybatisDefaultParameterHandler.java:71)     at com.baomidou.mybatisplus.MybatisXMLLanguageDriver.createParameterHandler(MybatisXMLLanguageDriver.java:37)     at org.apache.ibatis.session.Configuration.newParameterHandler(Configuration.java:545)     at org.apache.ibatis.executor.statement.BaseStatementHandler.<init>(BaseStatementHandler.java:69)     at org.apache.ibatis.executor.statement.PreparedStatementHandler.<init>(PreparedStatementHandler.java:40)     at org.apache.ibatis.executor.statement.RoutingStatementHandler.<init>(RoutingStatementHandler.java:46)     at org.apache.ibatis.session.Configuration.newStatementHandler(Configuration.java:558)     at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:48)     at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)     at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)     at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)     ... 43 more Caused by: java.lang.IllegalArgumentException: argument type mismatch     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:498)     at org.apache.ibatis.reflection.invoker.MethodInvoker.invoke(MethodInvoker.java:41)     at org.apache.ibatis.reflection.wrapper.BeanWrapper.setBeanProperty(BeanWrapper.java:180)     ... 58 more

实体类City的主键是Integer类型的,在进行insert操作时,MyBatis Plus自动生成了一个Long类型的主键id,导致参数类型不匹配,出现上述错误

经过查看日志和调试发现,MyBatis最终调用BeanWrapper的setBeanProperty方法,通过反射执行最终的插入操作(增删改查应该都是通过此处的反射,不过暂时只调试了insert方法)

private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {     try {         Invoker method = metaClass.getSetInvoker(prop.getName());         Object[] params = {value};         try {             method.invoke(object, params);         } catch (Throwable t) {             throw ExceptionUtil.unwrapThrowable(t);         }     } catch (Throwable t) {         throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);     } }

此处传入的object即为我们想要保存到数据库的实体信息(不带ID信息),value为主键信息,此时主键值已经是一个Long类型的值,我们接着向上看value是哪里传过来的

setBeanProperty是一个私有方法,在本类调用,查询看到set(PropertyTokenizer prop, Object value)方法调用了它

@Override public void set(PropertyTokenizer prop, Object value) {     if (prop.getIndex() != null) {         Object collection = resolveCollection(prop, object);         setCollectionValue(prop, collection, value);     } else {       setBeanProperty(prop, object, value);     } }

set方法中的value也是其他地方传入的

Caused by: org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.lemon.rabbit.model.City' with value '1017367047558582273' Cause: java.lang.IllegalArgumentException: argument type mismatch     at org.apache.ibatis.reflection.wrapper.BeanWrapper.setBeanProperty(BeanWrapper.java:185)     at org.apache.ibatis.reflection.wrapper.BeanWrapper.set(BeanWrapper.java:59)     at org.apache.ibatis.reflection.MetaObject.setValue(MetaObject.java:140)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.populateKeys(MybatisDefaultParameterHandler.java:217)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.processBatch(MybatisDefaultParameterHandler.java:156)     at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.<init>(MybatisDefaultParameterHandler.java:71)     at com.baomidou.mybatisplus.MybatisXMLLanguageDriver.createParameterHandler(MybatisXMLLanguageDriver.java:37)     at org.apache.ibatis.session.Configuration.newParameterHandler(Configuration.java:545)     at org.apache.ibatis.executor.statement.BaseStatementHandler.<init>(BaseStatementHandler.java:69)     at org.apache.ibatis.executor.statement.PreparedStatementHandler.<init>(PreparedStatementHandler.java:40)     at org.apache.ibatis.executor.statement.RoutingStatementHandler.<init>(RoutingStatementHandler.java:46)     at org.apache.ibatis.session.Configuration.newStatementHandler(Configuration.java:558)     at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:48)     at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)     at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)     at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)     ... 43 more

查看报错日志可以看到,BeanWrapper的上一层是MetaObject,我们找到MetaObject,看到在getValue方法中调用了BeanWrapper的set方法(BeanWrapper实现了ObjectWrapper)

public void setValue(String name, Object value) {     PropertyTokenizer prop = new PropertyTokenizer(name);     if (prop.hasNext()) {         MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());         if (metaValue == SystemMetaObject.NULL_META_OBJECT) {             if (value == null && prop.getChildren() != null) {                 // don't instantiate child path if value is null                 return;             } else {                 metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);             }         }         metaValue.setValue(prop.getChildren(), value);     } else {         objectWrapper.set(prop, value);     } }

MetaObject中的主键值也是上层调用传入的,继续根据错误日志向上看:MybatisDefaultParameterHandler

    /**      * <p>      * 自定义元对象填充控制器      * </p>      *      * @param metaObjectHandler 元数据填充处理器      * @param tableInfo         数据库表反射信息      * @param ms                MappedStatement      * @param parameterObject   插入数据库对象      * @return Object      */     protected static Object populateKeys(MetaObjectHandler metaObjectHandler, TableInfo tableInfo,                                          MappedStatement ms, Object parameterObject) {         if (null == tableInfo || StringUtils.isEmpty(tableInfo.getKeyProperty()) || null == tableInfo.getIdType()) {             /* 不处理 */             return parameterObject;         }         /* 自定义元对象填充控制器 */         MetaObject metaObject = ms.getConfiguration().newMetaObject(parameterObject);         if (ms.getSqlCommandType() == SqlCommandType.INSERT) {             if (tableInfo.getIdType().getKey() >= 2) {                 Object idValue = metaObject.getValue(tableInfo.getKeyProperty());                 /* 自定义 ID */                 if (StringUtils.checkValNull(idValue)) {                     if (tableInfo.getIdType() == IdType.ID_WORKER) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId());                     } else if (tableInfo.getIdType() == IdType.ID_WORKER_STR) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr());                     } else if (tableInfo.getIdType() == IdType.UUID) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID());                     }                 }             }             // 插入填充             if (metaObjectHandler.openInsertFill()) {                 metaObjectHandler.insertFill(metaObject);             }         } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE && metaObjectHandler.openUpdateFill()) {             // 更新填充             metaObjectHandler.updateFill(metaObject);         }         return metaObject.getOriginalObject();     }

在populateKeys方法中调用了metaObject.setValue()

                if (StringUtils.checkValNull(idValue)) {                     if (tableInfo.getIdType() == IdType.ID_WORKER) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId());                     } else if (tableInfo.getIdType() == IdType.ID_WORKER_STR) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr());                     } else if (tableInfo.getIdType() == IdType.UUID) {                         metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID());                     }                 }

可以看到,此处根据IdType生成不同类型的主键id,IdType是一个枚举类,定义了生成ID的类型

  • AUTO 数据库ID自增

  • INPUT 用户输入ID

  • ID_WORKER 全局唯一ID,Long类型的主键

  • ID_WORKER_STR 字符串全局唯一ID

  • UUID 全局唯一ID,UUID类型的主键

  • NONE 该类型为未设置主键类型

当IdType的类型为ID_WORKER、ID_WORKER_STR或者UUID时,主键由MyBatis Plus的IdWorker类生成

ID_WORKER

调用IdWorker的getId()方法,生成一个与时间相关的主键id

    public static long getId() {         return worker.nextId();     }

IdWorker的getId()方法引用了Sequence的nextId()方法

    public synchronized long nextId() {         long timestamp = timeGen();         if (timestamp < lastTimestamp) {//闰秒             long offset = lastTimestamp - timestamp;             if (offset <= 5) {                 try {                     wait(offset << 1);                     timestamp = timeGen();                     if (timestamp < lastTimestamp) {                         throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));                     }                 } catch (Exception e) {                     throw new RuntimeException(e);                 }             } else {                 throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));             }         }         if (lastTimestamp == timestamp) {             // 相同毫秒内,序列号自增             sequence = (sequence + 1) & sequenceMask;             if (sequence == 0) {                 // 同一毫秒的序列数已经达到最大                 timestamp = tilNextMillis(lastTimestamp);             }         } else {             // 不同毫秒内,序列号置为 1 - 3 随机数             sequence = ThreadLocalRandom.current().nextLong(1, 3);         }         lastTimestamp = timestamp;         return ((timestamp - twepoch) << timestampLeftShift)    // 时间戳部分             | (datacenterId << datacenterIdShift)           // 数据中心部分             | (workerId << workerIdShift)                   // 机器标识部分             | sequence;                                     // 序列号部分     }

ID_WORKER_STR

将worker.nextId()的返回值转化为字符串,和getId()方法相似

    public static String getIdStr() {         return String.valueOf(worker.nextId());     }

UUID

去除中划线的UUID字符串

    public static synchronized String get32UUID() {         return UUID.randomUUID().toString().replace("-", "");     }

此时,已经基本可以确认,问题出在IdType的配置上,那么这个IdType从哪里获取的呢?TableInfo中获取的!

TableInfo是数据库表反射信息实体类,此处由其他方法传入的,查看日志:

at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.populateKeys(MybatisDefaultParameterHandler.java:217) at com.baomidou.mybatisplus.MybatisDefaultParameterHandler.processBatch(MybatisDefaultParameterHandler.java:156)

通过日志可以看出,populateKeys()方法是在processBatch()方法中调用的,找到该方法

    /**      * <p>      * 批量(填充主键 ID)      * </p>      *      * @param ms      * @param parameterObject 插入数据库对象      * @return      */     protected static Object processBatch(MappedStatement ms, Object parameterObject) {         //检查parameterObject         if (null == parameterObject) {             return null;         }         boolean isFill = false;         // 全局配置是否配置填充器         MetaObjectHandler metaObjectHandler = GlobalConfigUtils.getMetaObjectHandler(ms.getConfiguration());         /* 只处理插入或更新操作 */         if (ms.getSqlCommandType() == SqlCommandType.INSERT) {             isFill = true;         } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE             && metaObjectHandler.openUpdateFill()) {             isFill = true;         }         if (isFill) {             Collection<Object> parameters = getParameters(parameterObject);             if (null != parameters) {                 List<Object> objList = new ArrayList<>();                 for (Object parameter : parameters) {                     TableInfo tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());                     if (null != tableInfo) {                         objList.add(populateKeys(metaObjectHandler, tableInfo, ms, parameter));                     } else {                         /*                          * 非表映射类不处理                          */                         objList.add(parameter);                     }                 }                 return objList;             } else {                 TableInfo tableInfo = null;                 if (parameterObject instanceof Map) {                     Map map = (Map) parameterObject;                     if (map.containsKey("et")) {                         Object et = map.get("et");                         if (et != null) {                             if (et instanceof Map) {                                 Map realEtMap = (Map) et;                                 if (realEtMap.containsKey("MP_OPTLOCK_ET_ORIGINAL")) {//refer to OptimisticLockerInterceptor.MP_OPTLOCK_ET_ORIGINAL                                     tableInfo = TableInfoHelper.getTableInfo(realEtMap.get("MP_OPTLOCK_ET_ORIGINAL").getClass());                                 }                             } else {                                 tableInfo = TableInfoHelper.getTableInfo(et.getClass());                             }                         }                     }                 } else {                     tableInfo = TableInfoHelper.getTableInfo(parameterObject.getClass());                 }                 return populateKeys(metaObjectHandler, tableInfo, ms, parameterObject);             }         }         return parameterObject;     }

下面对TableInfo生成的这段代码做个说明

                TableInfo tableInfo = null;                 if (parameterObject instanceof Map) {                     Map map = (Map) parameterObject;                     if (map.containsKey("et")) {                         Object et = map.get("et");                         if (et != null) {                             if (et instanceof Map) {                                 Map realEtMap = (Map) et;                                 if (realEtMap.containsKey("MP_OPTLOCK_ET_ORIGINAL")) {//refer to OptimisticLockerInterceptor.MP_OPTLOCK_ET_ORIGINAL                                     tableInfo = TableInfoHelper.getTableInfo(realEtMap.get("MP_OPTLOCK_ET_ORIGINAL").getClass());                                 }                             } else {                                 tableInfo = TableInfoHelper.getTableInfo(et.getClass());                             }                         }                     }                 } else {                     tableInfo = TableInfoHelper.getTableInfo(parameterObject.getClass());                 }

parameterObject:要保存到数据库中的实体类信息

我在保存数据时,参数形式并不是Map类型的,所以直接跳转到else中

tableInfo = TableInfoHelper.getTableInfo(parameterObject.getClass());

根据保存的实体类的类型去获取数据库表反射信息

我们看下getTableInfo()方法的实现

    public static TableInfo getTableInfo(Class<?> clazz) {         return tableInfoCache.get(ClassUtils.getUserClass(clazz).getName());     }

从tableInfoCache中获取指定类型的数据库反射信息。tableInfoCache是一个线程安全的私有静态Map,主要用于存放类型和数据库表的映射关系。断点调试到此处,看下tableInfoCache的内容

MyBatis Plus主键设置策略是什么

可以看到,idType的值为ID_WORKER,即生成一个与时间相关的Long类型的id。在放入到tableInfoCache的时候就已经指定了idType的值。

查看TableInfoHelper的源码可以得知,initTableInfo()方法负责initTableInfo的初始化

    public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {         //检测是否已存在         TableInfo tableInfo = tableInfoCache.get(clazz.getName());         if (StringUtils.checkValNotNull(tableInfo)) {             if (StringUtils.checkValNotNull(builderAssistant)) {                 tableInfo.setConfigMark(builderAssistant.getConfiguration());             }             return tableInfo;         }         tableInfo = new TableInfo();         GlobalConfiguration globalConfig;         if (null != builderAssistant) {             tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());             tableInfo.setConfigMark(builderAssistant.getConfiguration());             //获取全局配置,其中包括了idType             globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());         } else {             // 兼容测试场景             globalConfig = GlobalConfigUtils.DEFAULT;         }         /* 表名 */         TableName table = clazz.getAnnotation(TableName.class);         String tableName = clazz.getSimpleName();         if (table != null && StringUtils.isNotEmpty(table.value())) {             tableName = table.value();         } else {             // 开启字段下划线申明             if (globalConfig.isDbColumnUnderline()) {                 tableName = StringUtils.camelToUnderline(tableName);             }             // 大写命名判断             if (globalConfig.isCapitalMode()) {                 tableName = tableName.toUpperCase();             } else {                 // 首字母小写                 tableName = StringUtils.firstToLowerCase(tableName);             }             // 存在表前缀             if (null != globalConfig.getTablePrefix()) {                 tableName = globalConfig.getTablePrefix() + tableName;             }         }         tableInfo.setTableName(tableName);         // 开启了自定义 KEY 生成器         if (null != globalConfig.getKeyGenerator()) {             tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));         }         /* 表结果集映射 */         if (table != null && StringUtils.isNotEmpty(table.resultMap())) {             tableInfo.setResultMap(table.resultMap());         }         List<TableFieldInfo> fieldList = new ArrayList<>();         List<Field> list = getAllFields(clazz);         // 标记是否读取到主键         boolean isReadPK = false;         boolean existTableId = existTableId(list);         for (Field field : list) {             /*              * 主键ID 初始化              */             if (!isReadPK) {                 if (existTableId) {                     isReadPK = initTableId(globalConfig, tableInfo, field, clazz);                 } else {                     isReadPK = initFieldId(globalConfig, tableInfo, field, clazz);                 }                 if (isReadPK) {                     continue;                 }             }             /*              * 字段初始化              */             if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) {                 continue;             }             /*              * 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里              */             fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field));         }         /* 字段列表 */         tableInfo.setFieldList(globalConfig, fieldList);         /*          * 未发现主键注解,提示警告信息          */         if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {             logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));         }         /*          * 注入          */         tableInfoCache.put(clazz.getName(), tableInfo);         return tableInfo;     }

在existTableId方法中判断主键注解@TableId是否存在

    /**      * <p>      * 判断主键注解是否存在      * </p>      *      * @param list 字段列表      * @return      */     public static boolean existTableId(List<Field> list) {         boolean exist = false;         for (Field field : list) {             TableId tableId = field.getAnnotation(TableId.class);             if (tableId != null) {                 exist = true;                 break;             }         }         return exist;     }

当不存在主键注解时,会调用initFieldId()方法对主键属性进行初始化

    private static boolean initFieldId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {         String column = field.getName();         if (globalConfig.isCapitalMode()) {             column = column.toUpperCase();         }         if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) {             if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {                 tableInfo.setIdType(globalConfig.getIdType());                 tableInfo.setKeyColumn(column);                 tableInfo.setKeyProperty(field.getName());                 return true;             } else {                 throwExceptionId(clazz);             }         }         return false;     }

至此, 我们基本可以判断是因为在实体类City中id属性没有加@TableId注解,我们看下TableId的源码

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface TableId {     /**      * <p>      * 字段值(驼峰命名方式,该值可无)      * </p>      */     String value() default "";     /**      * <p>      * 主键ID      * </p>      * {@link IdType}      */     IdType type() default IdType.NONE; }

TableId的类型通过type来指定,默认是IdType.NONE(该类型为未设置主键类型),City表是通过数据库的自增序列实现的,所以设置为AUTO

@TableId(type = IdType.AUTO) private Integer id;

然后测试,程序正常运行,保存数据成功。

扩展

对于tableInfo默认的idType值配置,可以看出用的是全局配置的idType,全局配置的值是在initTableInfo()方法中获取的,有兴趣的话可以去看看全局配置的实现,此处暂不深入了

        GlobalConfiguration globalConfig;         if (null != builderAssistant) {             tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());             tableInfo.setConfigMark(builderAssistant.getConfiguration());             globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());         } else {             // 兼容测试场景             globalConfig = GlobalConfigUtils.DEFAULT;         }

关于“MyBatis Plus主键设置策略是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“MyBatis Plus主键设置策略是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI