Browse Source

封装 IdTypeEnvironmentPostProcessor 组件,自动适配 id-type 策略,用于 Oracle、PostgreSQL 的自动适配

YunaiV 3 years atrás
parent
commit
98c317f0e0

+ 4 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml

@@ -42,6 +42,10 @@
             <groupId>com.oracle.database.jdbc</groupId>
             <artifactId>ojdbc8</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>com.alibaba</groupId>

+ 73 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java

@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.framework.mybatis.config;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.IdType;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.env.EnvironmentPostProcessor;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+import java.util.Set;
+
+/**
+ * 当 IdType 为 {@link IdType#NONE} 时,根据 PRIMARY 数据源所使用的数据库,自动设置
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor {
+
+    private static final String ID_TYPE_KEY = "mybatis-plus.global-config.db-config.id-type";
+
+    private static final String DATASOURCE_DYNAMIC_KEY = "spring.datasource.dynamic";
+
+    private static final Set<DbType> INPUT_ID_TYPES = SetUtils.asSet(DbType.ORACLE, DbType.ORACLE_12C,
+            DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.DB2, DbType.H2);
+
+    @Override
+    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
+        // 如果非 NONE,则不进行处理
+        IdType idType = getIdType(environment);
+        if (idType != IdType.NONE) {
+            return;
+        }
+        // 如果获取不到 DbType,则不进行处理
+        DbType dbType = getDbType(environment);
+        if (dbType == null) {
+            return;
+        }
+
+        // 情况一,用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
+        if (INPUT_ID_TYPES.contains(dbType)) {
+            setIdType(environment, IdType.INPUT);
+            return;
+        }
+        // 情况二,自增 ID,适合 MySQL 等直接自增的数据库
+        setIdType(environment, IdType.AUTO);
+    }
+
+    public IdType getIdType(ConfigurableEnvironment environment) {
+        return environment.getProperty(ID_TYPE_KEY, IdType.class);
+    }
+
+    public void setIdType(ConfigurableEnvironment environment, IdType idType) {
+        environment.getSystemProperties().put(ID_TYPE_KEY, idType);
+        log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
+    }
+
+    public static DbType getDbType(ConfigurableEnvironment environment) {
+        String primary = environment.getProperty(DATASOURCE_DYNAMIC_KEY + "." + "primary");
+        if (StrUtil.isEmpty(primary)) {
+            return null;
+        }
+        String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".datasource." + primary + ".url");
+        if (StrUtil.isEmpty(url)) {
+            return null;
+        }
+        return JdbcUtils.getDbType(url);
+    }
+
+}

+ 30 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java

@@ -1,13 +1,22 @@
 package cn.iocoder.yudao.framework.mybatis.config;
 
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
+import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
+import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
+import com.baomidou.mybatisplus.extension.incrementer.KingbaseKeyGenerator;
+import com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator;
+import com.baomidou.mybatisplus.extension.incrementer.PostgreKeyGenerator;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import org.apache.ibatis.annotations.Mapper;
 import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.ConfigurableEnvironment;
 
 /**
  * MyBaits 配置类
@@ -31,4 +40,25 @@ public class YudaoMybatisAutoConfiguration {
         return new DefaultDBFieldHandler(); // 自动填充参数类
     }
 
+    @Bean
+    @ConditionalOnProperty(prefix = "mybatis-plus.global-config.db-config", name = "id-type", havingValue = "INPUT")
+    public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) {
+        DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment);
+        if (dbType != null) {
+            switch (dbType) {
+                case POSTGRE_SQL:
+                    return new PostgreKeyGenerator();
+                case ORACLE:
+                case ORACLE_12C:
+                    return new OracleKeyGenerator();
+                case H2:
+                    return new H2KeyGenerator();
+                case KINGBASE_ES:
+                    return new KingbaseKeyGenerator();
+            }
+        }
+        // 找不到合适的 IKeyGenerator 实现类
+        throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
+    }
+
 }

+ 13 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/JdbcUtils.java

@@ -1,5 +1,7 @@
 package cn.iocoder.yudao.framework.mybatis.core.util;
 
+import com.baomidou.mybatisplus.annotation.DbType;
+
 import java.sql.Connection;
 import java.sql.DriverManager;
 
@@ -26,4 +28,15 @@ public class JdbcUtils {
         }
     }
 
+    /**
+     * 获得 URL 对应的 DB 类型
+     *
+     * @param url URL
+     * @return DB 类型
+     */
+    public static DbType getDbType(String url) {
+        String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null);
+        return DbType.getDbType(name);
+    }
+
 }

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories

@@ -1,3 +1,5 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
   cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration,\
   cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration
+org.springframework.boot.env.EnvironmentPostProcessor=\
+  cn.iocoder.yudao.framework.mybatis.config.IdTypeEnvironmentPostProcessor

+ 11 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.system.framework.errorcode.core.generator;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.util.ClassUtil;
 import cn.hutool.core.util.ReflectUtil;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
@@ -57,7 +58,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
      *
      * @return 错误码数组
      */
-    private List<ErrorCodeAutoGenerateReqDTO>  parseErrorCode() {
+    private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() {
         // 校验 errorCodeConstantsClass 参数
         if (CollUtil.isEmpty(constantsClassList)) {
             log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]");
@@ -67,10 +68,15 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
         // 解析错误码
         List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
         constantsClassList.forEach(constantsClass -> {
-            // 解析错误码枚举类
-            Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
-            // 解析错误码
-            autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
+            try {
+                // 解析错误码枚举类
+                Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
+                // 解析错误码
+                autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
+            } catch (Exception ex) {
+                log.warn("[parseErrorCode][constantsClass({}) 加载失败({})]", constantsClass,
+                        ExceptionUtil.getRootCauseMessage(ex));
+            }
         });
         return autoGenerateDTOs;
     }

+ 5 - 5
yudao-server/pom.xml

@@ -43,11 +43,11 @@
             <version>${revision}</version>
         </dependency>
         <!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现-->
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-bpm-biz-flowable</artifactId>
-            <version>${revision}</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>cn.iocoder.boot</groupId>-->
+<!--            <artifactId>yudao-module-bpm-biz-flowable</artifactId>-->
+<!--            <version>${revision}</version>-->
+<!--        </dependency>-->
 <!--        <dependency>-->
 <!--            <groupId>cn.iocoder.boot</groupId>-->
 <!--            <artifactId>yudao-module-bpm-biz-activiti</artifactId>-->

+ 1 - 1
yudao-server/src/main/resources/application-dev.yaml

@@ -36,7 +36,7 @@ spring:
         time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
         min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
         max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
-        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        validation-query: SELECT 1 # 配置检测连接是否有效
         test-while-idle: true
         test-on-borrow: false
         test-on-return: false

+ 5 - 5
yudao-server/src/main/resources/application-local.yaml

@@ -36,7 +36,7 @@ spring:
         time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
         min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
         max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
-        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        validation-query: SELECT 1 # 配置检测连接是否有效
         test-while-idle: true
         test-on-borrow: false
         test-on-return: false
@@ -44,14 +44,14 @@ spring:
       datasource:
         master:
           name: ruoyi-vue-pro
-          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
-          driver-class-name: com.mysql.jdbc.Driver
+#          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL 连接的示例
+          url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
           username: root
           password: 123456
         slave: # 模拟从库,可根据自己需要修改
           name: ruoyi-vue-pro
-          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
-          driver-class-name: com.mysql.jdbc.Driver
+#          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL 连接的示例
+          url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
           username: root
           password: 123456
 

+ 4 - 1
yudao-server/src/main/resources/application.yaml

@@ -54,7 +54,10 @@ mybatis-plus:
     map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
   global-config:
     db-config:
-      id-type: AUTO # 自增 ID
+      id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+#      id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
+#      id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
+#      id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
       logic-delete-value: 1 # 逻辑已删除值(默认为 1)
       logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
   type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject