Commit 98c317f0e0775b6bc95d7fdcc6936ef9513a98b3

Authored by YunaiV
1 parent 2367280d

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

yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
... ... @@ -42,6 +42,10 @@
42 42 <groupId>com.oracle.database.jdbc</groupId>
43 43 <artifactId>ojdbc8</artifactId>
44 44 </dependency>
  45 + <dependency>
  46 + <groupId>org.postgresql</groupId>
  47 + <artifactId>postgresql</artifactId>
  48 + </dependency>
45 49  
46 50 <dependency>
47 51 <groupId>com.alibaba</groupId>
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.config;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  5 +import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
  6 +import com.baomidou.mybatisplus.annotation.DbType;
  7 +import com.baomidou.mybatisplus.annotation.IdType;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.boot.SpringApplication;
  10 +import org.springframework.boot.env.EnvironmentPostProcessor;
  11 +import org.springframework.core.env.ConfigurableEnvironment;
  12 +
  13 +import java.util.Set;
  14 +
  15 +/**
  16 + * 当 IdType 为 {@link IdType#NONE} 时,根据 PRIMARY 数据源所使用的数据库,自动设置
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Slf4j
  21 +public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor {
  22 +
  23 + private static final String ID_TYPE_KEY = "mybatis-plus.global-config.db-config.id-type";
  24 +
  25 + private static final String DATASOURCE_DYNAMIC_KEY = "spring.datasource.dynamic";
  26 +
  27 + private static final Set<DbType> INPUT_ID_TYPES = SetUtils.asSet(DbType.ORACLE, DbType.ORACLE_12C,
  28 + DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.DB2, DbType.H2);
  29 +
  30 + @Override
  31 + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
  32 + // 如果非 NONE,则不进行处理
  33 + IdType idType = getIdType(environment);
  34 + if (idType != IdType.NONE) {
  35 + return;
  36 + }
  37 + // 如果获取不到 DbType,则不进行处理
  38 + DbType dbType = getDbType(environment);
  39 + if (dbType == null) {
  40 + return;
  41 + }
  42 +
  43 + // 情况一,用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
  44 + if (INPUT_ID_TYPES.contains(dbType)) {
  45 + setIdType(environment, IdType.INPUT);
  46 + return;
  47 + }
  48 + // 情况二,自增 ID,适合 MySQL 等直接自增的数据库
  49 + setIdType(environment, IdType.AUTO);
  50 + }
  51 +
  52 + public IdType getIdType(ConfigurableEnvironment environment) {
  53 + return environment.getProperty(ID_TYPE_KEY, IdType.class);
  54 + }
  55 +
  56 + public void setIdType(ConfigurableEnvironment environment, IdType idType) {
  57 + environment.getSystemProperties().put(ID_TYPE_KEY, idType);
  58 + log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
  59 + }
  60 +
  61 + public static DbType getDbType(ConfigurableEnvironment environment) {
  62 + String primary = environment.getProperty(DATASOURCE_DYNAMIC_KEY + "." + "primary");
  63 + if (StrUtil.isEmpty(primary)) {
  64 + return null;
  65 + }
  66 + String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".datasource." + primary + ".url");
  67 + if (StrUtil.isEmpty(url)) {
  68 + return null;
  69 + }
  70 + return JdbcUtils.getDbType(url);
  71 + }
  72 +
  73 +}
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
1 1 package cn.iocoder.yudao.framework.mybatis.config;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
  5 +import com.baomidou.mybatisplus.annotation.DbType;
4 6 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
  7 +import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
  8 +import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
  9 +import com.baomidou.mybatisplus.extension.incrementer.KingbaseKeyGenerator;
  10 +import com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator;
  11 +import com.baomidou.mybatisplus.extension.incrementer.PostgreKeyGenerator;
5 12 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
6 13 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
7 14 import org.apache.ibatis.annotations.Mapper;
8 15 import org.mybatis.spring.annotation.MapperScan;
  16 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9 17 import org.springframework.context.annotation.Bean;
10 18 import org.springframework.context.annotation.Configuration;
  19 +import org.springframework.core.env.ConfigurableEnvironment;
11 20  
12 21 /**
13 22 * MyBaits 配置类
... ... @@ -31,4 +40,25 @@ public class YudaoMybatisAutoConfiguration {
31 40 return new DefaultDBFieldHandler(); // 自动填充参数类
32 41 }
33 42  
  43 + @Bean
  44 + @ConditionalOnProperty(prefix = "mybatis-plus.global-config.db-config", name = "id-type", havingValue = "INPUT")
  45 + public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) {
  46 + DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment);
  47 + if (dbType != null) {
  48 + switch (dbType) {
  49 + case POSTGRE_SQL:
  50 + return new PostgreKeyGenerator();
  51 + case ORACLE:
  52 + case ORACLE_12C:
  53 + return new OracleKeyGenerator();
  54 + case H2:
  55 + return new H2KeyGenerator();
  56 + case KINGBASE_ES:
  57 + return new KingbaseKeyGenerator();
  58 + }
  59 + }
  60 + // 找不到合适的 IKeyGenerator 实现类
  61 + throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
  62 + }
  63 +
34 64 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/JdbcUtils.java
1 1 package cn.iocoder.yudao.framework.mybatis.core.util;
2 2  
  3 +import com.baomidou.mybatisplus.annotation.DbType;
  4 +
3 5 import java.sql.Connection;
4 6 import java.sql.DriverManager;
5 7  
... ... @@ -26,4 +28,15 @@ public class JdbcUtils {
26 28 }
27 29 }
28 30  
  31 + /**
  32 + * 获得 URL 对应的 DB 类型
  33 + *
  34 + * @param url URL
  35 + * @return DB 类型
  36 + */
  37 + public static DbType getDbType(String url) {
  38 + String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null);
  39 + return DbType.getDbType(name);
  40 + }
  41 +
29 42 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/resources/META-INF/spring.factories
1 1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 2 cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration,\
3 3 cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration
  4 +org.springframework.boot.env.EnvironmentPostProcessor=\
  5 + cn.iocoder.yudao.framework.mybatis.config.IdTypeEnvironmentPostProcessor
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java
1 1 package cn.iocoder.yudao.module.system.framework.errorcode.core.generator;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.exceptions.ExceptionUtil;
4 5 import cn.hutool.core.util.ClassUtil;
5 6 import cn.hutool.core.util.ReflectUtil;
6 7 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
... ... @@ -57,7 +58,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
57 58 *
58 59 * @return 错误码数组
59 60 */
60   - private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() {
  61 + private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() {
61 62 // 校验 errorCodeConstantsClass 参数
62 63 if (CollUtil.isEmpty(constantsClassList)) {
63 64 log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]");
... ... @@ -67,10 +68,15 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
67 68 // 解析错误码
68 69 List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
69 70 constantsClassList.forEach(constantsClass -> {
70   - // 解析错误码枚举类
71   - Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
72   - // 解析错误码
73   - autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
  71 + try {
  72 + // 解析错误码枚举类
  73 + Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
  74 + // 解析错误码
  75 + autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
  76 + } catch (Exception ex) {
  77 + log.warn("[parseErrorCode][constantsClass({}) 加载失败({})]", constantsClass,
  78 + ExceptionUtil.getRootCauseMessage(ex));
  79 + }
74 80 });
75 81 return autoGenerateDTOs;
76 82 }
... ...
yudao-server/pom.xml
... ... @@ -43,11 +43,11 @@
43 43 <version>${revision}</version>
44 44 </dependency>
45 45 <!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现-->
46   - <dependency>
47   - <groupId>cn.iocoder.boot</groupId>
48   - <artifactId>yudao-module-bpm-biz-flowable</artifactId>
49   - <version>${revision}</version>
50   - </dependency>
  46 +<!-- <dependency>-->
  47 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  48 +<!-- <artifactId>yudao-module-bpm-biz-flowable</artifactId>-->
  49 +<!-- <version>${revision}</version>-->
  50 +<!-- </dependency>-->
51 51 <!-- <dependency>-->
52 52 <!-- <groupId>cn.iocoder.boot</groupId>-->
53 53 <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>-->
... ...
yudao-server/src/main/resources/application-dev.yaml
... ... @@ -36,7 +36,7 @@ spring:
36 36 time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
37 37 min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
38 38 max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
39   - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
  39 + validation-query: SELECT 1 # 配置检测连接是否有效
40 40 test-while-idle: true
41 41 test-on-borrow: false
42 42 test-on-return: false
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -36,7 +36,7 @@ spring:
36 36 time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
37 37 min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
38 38 max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
39   - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
  39 + validation-query: SELECT 1 # 配置检测连接是否有效
40 40 test-while-idle: true
41 41 test-on-borrow: false
42 42 test-on-return: false
... ... @@ -44,14 +44,14 @@ spring:
44 44 datasource:
45 45 master:
46 46 name: ruoyi-vue-pro
47   - 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
48   - driver-class-name: com.mysql.jdbc.Driver
  47 +# 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 连接的示例
  48 + url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
49 49 username: root
50 50 password: 123456
51 51 slave: # 模拟从库,可根据自己需要修改
52 52 name: ruoyi-vue-pro
53   - 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
54   - driver-class-name: com.mysql.jdbc.Driver
  53 +# 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 连接的示例
  54 + url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
55 55 username: root
56 56 password: 123456
57 57  
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -54,7 +54,10 @@ mybatis-plus:
54 54 map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
55 55 global-config:
56 56 db-config:
57   - id-type: AUTO # 自增 ID
  57 + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
  58 +# id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
  59 +# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
  60 +# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
58 61 logic-delete-value: 1 # 逻辑已删除值(默认为 1)
59 62 logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
60 63 type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
... ...