当前位置: 首页 > news >正文

mybatisplus oracle 数据库OracleKeyGenerator使用序列生成主键原理

目录

  • 一、启动流程
  • 二、执行流程
  • 三、总结

一、启动流程

1、在mybaitsplus自动配置类MybatisPlusAutoConfiguration.java中创建SqlSessionFactory部分代码如下:

    applySqlSessionFactoryBeanCustomizers(factory);GlobalConfig globalConfig = this.properties.getGlobalConfig();this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler);this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler);this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);factory.setGlobalConfig(globalConfig);return factory.getObject();

可以看到 this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));会从IOC容器中获取IKeyGenerator组件放入到globalConfig中。下面是OracleKeyGenerator 类实现了IKeyGenerator接口。

public class OracleKeyGenerator implements IKeyGenerator {@Overridepublic String executeSql(String incrementerName) {return "SELECT " + incrementerName + ".NEXTVAL FROM DUAL";}@Overridepublic DbType dbType() {return DbType.ORACLE;}
}

要想让OracleKeyGenerator 生效,需要把OracleKeyGenerator 加入都Ioc容器。

  @Beanpublic OracleKeyGenerator oracleKeyGenerator(){return new OracleKeyGenerator();}

实体类也需要使用注解@KeySequence配置序列的名称,@TableId中type是IdType.INPUT

@KeySequence(value = "SEQ_ORACLE_STRING_KEY",dbType = DbType.ORACLE)
public class MStudent implements Serializable {private static final long serialVersionUID = -57527430509435738L;@TableId(type = IdType.INPUT)private String id;private String name;private Integer age;

下面继续看mybatisplus启动流程如下图调用栈
在这里插入图片描述
MybatisSqlSessionFactoryBean.java中getObject()->afterPropertiesSet()->buildSqlSessionFactory()-> xmlMapperBuilder.parse()->MybatisXMLMapperBuilder中parse->bindMapperForNamespace()->
configuration.addMapper(boundType)->MybatisConfiguration中addMapper->
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
parser.parse();没有XML配置文件注入基础CRUD方法
在parse中会判断正在解析的mapper.java是不是 com.baomidou.mybatisplus.core.mapper.Mapper的子类。也就是说自己的Mapper.java需要实现com.baomidou.mybatisplus.core.mapper.Mapper接口,才能被mybatisplus扩展crud方法。
在这里插入图片描述
继续看parserInjector方法,其中看一下 List methodList = this.getMethodList(mapperClass, tableInfo);方法获取所有mybatisplus拓展crud SQL 默认注入器。

    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);if (modelClass != null) {String className = mapperClass.toString();Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());if (!mapperRegistryCache.contains(className)) {TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);// 兼容旧代码if (CollectionUtils.isEmpty(methodList)) {methodList = this.getMethodList(builderAssistant.getConfiguration(), mapperClass, tableInfo);}if (CollectionUtils.isNotEmpty(methodList)) {// 循环注入自定义方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));} else {logger.debug(className + ", No effective injection method was found.");}mapperRegistryCache.add(className);}}}

默认的sql注入器

public class DefaultSqlInjector extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {GlobalConfig.DbConfig dbConfig = GlobalConfigUtils.getDbConfig(configuration);Stream.Builder<AbstractMethod> builder = Stream.<AbstractMethod>builder().add(new Insert(dbConfig.isInsertIgnoreAutoIncrementColumn())).add(new Delete()).add(new Update()).add(new SelectCount()).add(new SelectMaps()).add(new SelectObjs()).add(new SelectList());if (tableInfo.havePK()) {builder.add(new DeleteById()).add(new DeleteByIds()).add(new UpdateById()).add(new SelectById()).add(new SelectByIds());} else {logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",tableInfo.getEntityType()));}return builder.build().collect(toList());}
}

如下图1是使用反射解析实体类,把实体中属性及注解解析到TableInfo中,其中包含属性对应数据字段和主键信息。2中是循环遍历sql注入器的inject方法,咱们重点看insert.java sql注入器.
在这里插入图片描述
跟踪inject方法进入insert中injectMappedStatement,通过实体中的注解配置会进入图片选中的行, keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);

在这里插入图片描述
genKeyGenerator中会从GlobalConfig获取IKeyGenerator类型的组件,上面已经添加了OracleKeyGenerator,所以会获取到OracleKeyGenerator,下面会创建MappedStatement 其中掉用keyGenerator.executeSql(tableInfo.getKeySequence().value()))会调用OracleKeyGenerator中的public String executeSql(String incrementerName) {
return "SELECT " + incrementerName + “.NEXTVAL FROM DUAL”;
}方法。最终创建了id=com.ruoyi.system.mapper.MStudentMapper.insert!selectKey的MappedStatement 加入到configuration,创建了new SelectKeyGenerator(mappedStatement, true)放入了id=com.ruoyi.system.mapper.MStudentMapper.insert中的MappedStatement 的keyGenerator属性中。

public static KeyGenerator genKeyGenerator(String baseStatementId, TableInfo tableInfo, MapperBuilderAssistant builderAssistant) {List<IKeyGenerator> keyGenerators = GlobalConfigUtils.getKeyGenerators(builderAssistant.getConfiguration());if (CollectionUtils.isEmpty(keyGenerators)) {throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");}IKeyGenerator keyGenerator = null;if (keyGenerators.size() > 1) {// 多个主键生成器KeySequence keySequence = tableInfo.getKeySequence();if (null != keySequence && DbType.OTHER != keySequence.dbType()) {keyGenerator = keyGenerators.stream().filter(k -> k.dbType() == keySequence.dbType()).findFirst().orElse(null);}}// 无法找到注解指定生成器,默认使用第一个生成器if (null == keyGenerator) {keyGenerator = keyGenerators.get(0);}Configuration configuration = builderAssistant.getConfiguration();String id = builderAssistant.getCurrentNamespace() + StringPool.DOT + baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;ResultMap resultMap = new ResultMap.Builder(builderAssistant.getConfiguration(), id, tableInfo.getKeyType(), new ArrayList<>()).build();MappedStatement mappedStatement = new MappedStatement.Builder(builderAssistant.getConfiguration(), id,new StaticSqlSource(configuration, keyGenerator.executeSql(tableInfo.getKeySequence().value())), SqlCommandType.SELECT).keyProperty(tableInfo.getKeyProperty()).resultMaps(Collections.singletonList(resultMap)).build();configuration.addMappedStatement(mappedStatement);return new SelectKeyGenerator(mappedStatement, true);}

总结通过上面的流程,可以看到mybatisplus在解析实现了mapper接口的mapper时执行crud 的sql注入器,例如insert sql注入器,给当前解析的mapper添加了insert的MappedStatement。
现在咱们记住在id=com.ruoyi.system.mapper.MStudentMapper.insert中的MappedStatement 的属性keyGenerator中存放了SelectKeyGenerator(mappedStatement, true)其中MappedStatement是 id=com.ruoyi.system.mapper.MStudentMapper.insert!selectKey,这个MappedStatement中存入查询序列的sql。后面执行插入的时候会用到。

二、执行流程

mybatisplus中把mapper通过jdk代理生成MybatisMapperProxy.java,使用mybatisplus拓展的增删改查方法时,会调用MybatisMapperProxy中的invoke,最终调用MybatisMapperMethod.execute->org.mybatis.spring.SqlSessionTemplate.insert->org.apache.ibatis.session.defaults.DefaultSqlSession.insert跟踪到org.apache.ibatis.executor.SimpleExecutor.doUpdate
在这里插入图片描述
BaseStatementHandler构造器中执行generateKeys
在这里插入图片描述
这个地方就是从id=com.ruoyi.system.mapper.MStudentMapper.insert中MappedStatement 获取属性keyGenerator,上面介绍了是SelectKeyGenerator对象。

protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();}

所以来到SelectKeyGenerator中的processBefore方法。
从创建SelectKeyGenerator执行构造器可以看到,类中的keyStatement就是

id=com.ruoyi.system.mapper.MStudentMapper.insert!selectKey的mappedStatement
在这里插入图片描述
获取了simpleExecutor,使用simpleExecutor执行了query方法,传入了查询序列的sql,执行查询序列sql,也就是执行了SELECT SEQ_ORACLE_STRING_KEY.NEXTVAL FROM DUAL,将返回值,设置到了参数里面。
在这里插入图片描述

三、总结

1、在mybatisplus启动时,解析mapper的逻辑里面添加了判断是否实现了com.baomidou.mybatisplus.core.mapper.Mapper,只有实现了com.baomidou.mybatisplus.core.mapper.Mapper接口mybatisplus才会自动添加crud注入器(使用sql注入器insert.java delete.java …)添加相应的mappedStatement到configuration中,BaseMapper中有相应的拓展出的方法。在执行insert注入器时,根据OracleKeyGenerator获取查询序列sql,创建了查询序列的mappedStatement添加到了configuration,并把包装后的类SelectKeyGenerator放入了insert的mappedStatement的keyGenerator属性。
2、执行insert,在处理参数时,会调用SelectKeyGenerator执行序列sql获取序列返回的主键设置到参数中为insert插入数据使用。
在这里插入图片描述

http://www.dtcms.com/a/337361.html

相关文章:

  • Redis-缓存-穿透-布隆过滤器
  • Linux 系统(如 Ubuntu / CentOS)阿里云虚拟机(ECS)上部署 Bitnami LAMP
  • 用随机森林填补缺失值:原理、实现与实战
  • 大型语言模型(LLM)存在演示位置偏差:相同示例在提示中位置不同会导致模型预测结果和准确率显著变化
  • 基于NLP的文本生成系统设计与实现(LW+源码+讲解+部署)
  • 牛津大学xDeepMind 自然语言处理(1)
  • 【论文阅读69】-DeepHGNN复杂分层结构下的预测
  • 力扣 hot100 Day77
  • 深入浅出讲透IPD:三层逻辑实例详解 —— 卫朋
  • Mysql实战案例 | 利用Mycat实现MYSQL的读写分离
  • 计算机视觉(9)-实践中遇到的问题(六路相机模型采集训练部署全流程)
  • Linux命令大全-rm命令
  • Java发送企业微信通知
  • Python开篇:2024全链路指南,从入门到架构解锁未来
  • 搜索插入位置
  • 楼宇自控行业是智能建筑关键部分,发展前景向好
  • 数据结构(03)——线性表(顺序存储和链式存储)
  • 45 C++ STL模板库14-容器6-容器适配器-优先队列(priority_queue)
  • 力扣(LeetCode) ——100. 相同的树(C语言)
  • 算法-每日一题(DAY13)两数之和
  • 排序(Java实现)
  • Dijkstra和多层图 0
  • 蓝桥杯算法之搜索章 - 7
  • LeetCode 45.跳跃游戏II:贪心策略下的最少跳跃次数求解
  • 华为云服务器从注册到部署全流程指南
  • Pomian语言处理器 研发笔记(一):使用C++的正则表达式构建词法分析器
  • 零基础数据结构与算法——第七章:算法实践与工程应用-图像处理
  • LLM - MCP传输协议解读:从SSE的单向奔赴到Streamable HTTP的双向融合
  • JavaScript 原型继承与属性访问规则详解
  • ES入门教程