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

pull/2/head
YunaiV 2022-04-30 11:38:21 +08:00
parent 2367280d83
commit 98c317f0e0
10 changed files with 148 additions and 17 deletions

View File

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

View File

@ -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);
}
}

View File

@ -1,13 +1,22 @@
package cn.iocoder.yudao.framework.mybatis.config; package cn.iocoder.yudao.framework.mybatis.config;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; 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.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.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
/** /**
* MyBaits * MyBaits
@ -31,4 +40,25 @@ public class YudaoMybatisAutoConfiguration {
return new DefaultDBFieldHandler(); // 自动填充参数类 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));
}
} }

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.mybatis.core.util; package cn.iocoder.yudao.framework.mybatis.core.util;
import com.baomidou.mybatisplus.annotation.DbType;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; 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);
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -54,7 +54,10 @@ mybatis-plus:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config: global-config:
db-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-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject