Commit 98c317f0e0775b6bc95d7fdcc6936ef9513a98b3
1 parent
2367280d
封装 IdTypeEnvironmentPostProcessor 组件,自动适配 id-type 策略,用于 Oracle、PostgreSQL 的自动适配
Showing
10 changed files
with
148 additions
and
17 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
| @@ -42,6 +42,10 @@ | @@ -42,6 +42,10 @@ | ||
| 42 | <groupId>com.oracle.database.jdbc</groupId> | 42 | <groupId>com.oracle.database.jdbc</groupId> |
| 43 | <artifactId>ojdbc8</artifactId> | 43 | <artifactId>ojdbc8</artifactId> |
| 44 | </dependency> | 44 | </dependency> |
| 45 | + <dependency> | ||
| 46 | + <groupId>org.postgresql</groupId> | ||
| 47 | + <artifactId>postgresql</artifactId> | ||
| 48 | + </dependency> | ||
| 45 | 49 | ||
| 46 | <dependency> | 50 | <dependency> |
| 47 | <groupId>com.alibaba</groupId> | 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 | package cn.iocoder.yudao.framework.mybatis.config; | 1 | package cn.iocoder.yudao.framework.mybatis.config; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; | 4 | import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; |
| 5 | +import com.baomidou.mybatisplus.annotation.DbType; | ||
| 4 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; | 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 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; | 12 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 6 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; | 13 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
| 7 | import org.apache.ibatis.annotations.Mapper; | 14 | import org.apache.ibatis.annotations.Mapper; |
| 8 | import org.mybatis.spring.annotation.MapperScan; | 15 | import org.mybatis.spring.annotation.MapperScan; |
| 16 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| 9 | import org.springframework.context.annotation.Bean; | 17 | import org.springframework.context.annotation.Bean; |
| 10 | import org.springframework.context.annotation.Configuration; | 18 | import org.springframework.context.annotation.Configuration; |
| 19 | +import org.springframework.core.env.ConfigurableEnvironment; | ||
| 11 | 20 | ||
| 12 | /** | 21 | /** |
| 13 | * MyBaits 配置类 | 22 | * MyBaits 配置类 |
| @@ -31,4 +40,25 @@ public class YudaoMybatisAutoConfiguration { | @@ -31,4 +40,25 @@ public class YudaoMybatisAutoConfiguration { | ||
| 31 | return new DefaultDBFieldHandler(); // 自动填充参数类 | 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 | package cn.iocoder.yudao.framework.mybatis.core.util; | 1 | package cn.iocoder.yudao.framework.mybatis.core.util; |
| 2 | 2 | ||
| 3 | +import com.baomidou.mybatisplus.annotation.DbType; | ||
| 4 | + | ||
| 3 | import java.sql.Connection; | 5 | import java.sql.Connection; |
| 4 | import java.sql.DriverManager; | 6 | import java.sql.DriverManager; |
| 5 | 7 | ||
| @@ -26,4 +28,15 @@ public class JdbcUtils { | @@ -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 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
| 2 | cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration,\ | 2 | cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration,\ |
| 3 | cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration | 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 | package cn.iocoder.yudao.module.system.framework.errorcode.core.generator; | 1 | package cn.iocoder.yudao.module.system.framework.errorcode.core.generator; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.exceptions.ExceptionUtil; | ||
| 4 | import cn.hutool.core.util.ClassUtil; | 5 | import cn.hutool.core.util.ClassUtil; |
| 5 | import cn.hutool.core.util.ReflectUtil; | 6 | import cn.hutool.core.util.ReflectUtil; |
| 6 | import cn.iocoder.yudao.framework.common.exception.ErrorCode; | 7 | import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| @@ -57,7 +58,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator { | @@ -57,7 +58,7 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator { | ||
| 57 | * | 58 | * |
| 58 | * @return 错误码数组 | 59 | * @return 错误码数组 |
| 59 | */ | 60 | */ |
| 60 | - private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() { | 61 | + private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() { |
| 61 | // 校验 errorCodeConstantsClass 参数 | 62 | // 校验 errorCodeConstantsClass 参数 |
| 62 | if (CollUtil.isEmpty(constantsClassList)) { | 63 | if (CollUtil.isEmpty(constantsClassList)) { |
| 63 | log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]"); | 64 | log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]"); |
| @@ -67,10 +68,15 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator { | @@ -67,10 +68,15 @@ public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator { | ||
| 67 | // 解析错误码 | 68 | // 解析错误码 |
| 68 | List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>(); | 69 | List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>(); |
| 69 | constantsClassList.forEach(constantsClass -> { | 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 | return autoGenerateDTOs; | 81 | return autoGenerateDTOs; |
| 76 | } | 82 | } |
yudao-server/pom.xml
| @@ -43,11 +43,11 @@ | @@ -43,11 +43,11 @@ | ||
| 43 | <version>${revision}</version> | 43 | <version>${revision}</version> |
| 44 | </dependency> | 44 | </dependency> |
| 45 | <!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现--> | 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 | <!-- <dependency>--> | 51 | <!-- <dependency>--> |
| 52 | <!-- <groupId>cn.iocoder.boot</groupId>--> | 52 | <!-- <groupId>cn.iocoder.boot</groupId>--> |
| 53 | <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>--> | 53 | <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>--> |
yudao-server/src/main/resources/application-dev.yaml
| @@ -36,7 +36,7 @@ spring: | @@ -36,7 +36,7 @@ spring: | ||
| 36 | time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 | 36 | time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 |
| 37 | min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 | 37 | min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 |
| 38 | max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 | 38 | max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 |
| 39 | - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 | 39 | + validation-query: SELECT 1 # 配置检测连接是否有效 |
| 40 | test-while-idle: true | 40 | test-while-idle: true |
| 41 | test-on-borrow: false | 41 | test-on-borrow: false |
| 42 | test-on-return: false | 42 | test-on-return: false |
yudao-server/src/main/resources/application-local.yaml
| @@ -36,7 +36,7 @@ spring: | @@ -36,7 +36,7 @@ spring: | ||
| 36 | time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 | 36 | time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 |
| 37 | min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 | 37 | min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 |
| 38 | max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 | 38 | max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 |
| 39 | - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 | 39 | + validation-query: SELECT 1 # 配置检测连接是否有效 |
| 40 | test-while-idle: true | 40 | test-while-idle: true |
| 41 | test-on-borrow: false | 41 | test-on-borrow: false |
| 42 | test-on-return: false | 42 | test-on-return: false |
| @@ -44,14 +44,14 @@ spring: | @@ -44,14 +44,14 @@ spring: | ||
| 44 | datasource: | 44 | datasource: |
| 45 | master: | 45 | master: |
| 46 | name: ruoyi-vue-pro | 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 | username: root | 49 | username: root |
| 50 | password: 123456 | 50 | password: 123456 |
| 51 | slave: # 模拟从库,可根据自己需要修改 | 51 | slave: # 模拟从库,可根据自己需要修改 |
| 52 | name: ruoyi-vue-pro | 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 | username: root | 55 | username: root |
| 56 | password: 123456 | 56 | password: 123456 |
| 57 | 57 |
yudao-server/src/main/resources/application.yaml
| @@ -54,7 +54,10 @@ mybatis-plus: | @@ -54,7 +54,10 @@ mybatis-plus: | ||
| 54 | map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 | 54 | map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 |
| 55 | global-config: | 55 | global-config: |
| 56 | db-config: | 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 | logic-delete-value: 1 # 逻辑已删除值(默认为 1) | 61 | logic-delete-value: 1 # 逻辑已删除值(默认为 1) |
| 59 | logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) | 62 | logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) |
| 60 | type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject | 63 | type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject |