Browse Source

!1009 数据库 findInSet 兼容
Merge pull request !1009 from 芋道源码/feature/db

芋道源码 2 years ago
parent
commit
3d44c2f9e3

File diff suppressed because it is too large
+ 4723 - 0
sql/opengauss/ruoyi-vue-pro.sql


+ 12 - 5
sql/tools/README.md

@@ -55,7 +55,7 @@ docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
 ```Bash
 docker compose up -d dm8
 # 注意:启动完 dm 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
-docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql"
+docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql'
 exit
 ```
 
@@ -67,7 +67,8 @@ exit
 
 ① 下载人大金仓 Docker 镜像:
 
-> x86_64 版本: https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar 
+> x86_64 版本: https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar
+
 > aarch64 版本:https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/aarch64/kdb_aarch64_V009R001C001B0025.tar
 
 ② 加载镜像文件,在镜像 tar 文件所在目录运行:
@@ -80,11 +81,17 @@ docker load -i x86_64/kdb_x86_64_V009R001C001B0025.tar
 
 ```Bash
 docker compose up -d kingbase
-# 注意:启动完 kingbase 后,需要手动再执行如下命令,因为 dm 不支持初始化脚本
-docker compose exec kingbase bash -c "exec ksql -Uroot -d test -f /tmp/schema.sql"
+# 注意:启动完 kingbase 后,需要手动再执行如下命令
+docker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql'
 ```
 
-**注意**: MyBatis、MyBatis Plus 目前不兼容人大金仓,推荐直接使用 PostgreSQL JDBC 驱动,已经 url 配置方式连接数据库。
+### 1.7 华为 OpenGauss
+
+```Bash
+docker compose up -d opengauss
+# 注意:启动完 opengauss 后,需要手动再执行如下命令
+docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'
+```
 
 ## 1.X 容器的销毁重建
 

+ 9 - 1
sql/tools/convertor.py

@@ -804,13 +804,19 @@ CREATE TABLE {table_name} (
         return script
 
 
+class OpengaussConvertor(KingbaseConvertor):
+    def __init__(self, src):
+        super().__init__(src)
+        self.db_type = "OpenGauss"
+
+
 def main():
     parser = argparse.ArgumentParser(description="芋道系统数据库转换工具")
     parser.add_argument(
         "type",
         type=str,
         help="目标数据库类型",
-        choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase"],
+        choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase", "opengauss"],
     )
     args = parser.parse_args()
 
@@ -826,6 +832,8 @@ def main():
         convertor = DM8Convertor(sql_file)
     elif args.type == "kingbase":
         convertor = KingbaseConvertor(sql_file)
+    elif args.type == "opengauss":
+        convertor = OpengaussConvertor(sql_file)
     else:
         raise NotImplementedError(f"不支持目标数据库类型: {args.type}")
 

+ 18 - 3
sql/tools/docker-compose.yaml

@@ -6,6 +6,7 @@ volumes:
     sqlserver: { }
     dm8: { }
     kingbase: { }
+    opengauss: { }
 
 services:
     mysql:
@@ -92,7 +93,7 @@ services:
         volumes:
             - dm8:/opt/dmdbms/data
             - ../dm/ruoyi-vue-pro-dm8.sql:/tmp/schema.sql:ro
-            # docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql"
+            # docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql'
 
     kingbase:
         # x86_64: https://kingbase.oss-cn-beijing.aliyuncs.com/KESV8R3/V009R001C001B0025-安装包-docker/x86_64/kdb_x86_64_V009R001C001B0025.tar
@@ -106,6 +107,20 @@ services:
         ports:
             - "54321:54321"
         volumes:
-            - kingbase:/home/kingbase/userdata/
+            - kingbase:/home/kingbase/userdata
             - ../kingbase/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
-            # docker compose exec kingbase bash -c "exec ksql -Uroot -d test -f /tmp/schema.sql"
+            # docker compose exec kingbase bash -c 'ksql -U $DB_USER -d test -f /tmp/schema.sql'
+
+    opengauss:
+        image: opengauss/opengauss:5.0.0
+        restart: unless-stopped
+        environment:
+            GS_USERNAME: root
+            GS_PASSWORD: Yudao@2024
+            LD_LIBRARY_PATH: /usr/local/opengauss/lib:/usr/lib
+        ports:
+            - "5432:5432"
+        volumes:
+            - opengauss:/var/lib/opengauss
+            - ../opengauss/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
+            # docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'

+ 14 - 0
yudao-dependencies/pom.xml

@@ -30,6 +30,8 @@
         <easy-trans.version>2.2.11</easy-trans.version>
         <redisson.version>3.26.0</redisson.version>
         <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
+        <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
+        <opengauss.jdbc.version>5.0.2</opengauss.jdbc.version>
         <!-- 消息队列 -->
         <rocketmq-spring.version>2.3.0</rocketmq-spring.version>
         <!-- 服务保障相关 -->
@@ -242,6 +244,18 @@
                 <version>${dm8.jdbc.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.opengauss</groupId>
+                <artifactId>opengauss-jdbc</artifactId>
+                <version>${opengauss.jdbc.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.com.kingbase</groupId>
+                <artifactId>kingbase8</artifactId>
+                <version>${kingbase.jdbc.version}</version>
+            </dependency>
+
             <!-- Job 定时任务相关 -->
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>

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

@@ -53,6 +53,16 @@
             <artifactId>DmJdbcDriver18</artifactId>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>cn.com.kingbase</groupId>
+            <artifactId>kingbase8</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.opengauss</groupId>
+            <artifactId>opengauss-jdbc</artifactId>
+            <optional>true</optional>
+        </dependency>
 
         <dependency>
             <groupId>com.alibaba</groupId>

+ 84 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/enums/DbTypeEnum.java

@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.framework.mybatis.core.enums;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.annotation.DbType;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 针对 MyBatis Plus 的 {@link DbType} 增强,补充更多信息
+ */
+@Getter
+@AllArgsConstructor
+public enum DbTypeEnum {
+
+    /**
+     * MySQL
+     */
+    MY_SQL( DbType.MYSQL, "MySQL", "FIND_IN_SET('#{value}', #{column}) <> 0"),
+
+    /**
+     * Oracle
+     */
+    ORACLE(DbType.ORACLE, "Oracle", "FIND_IN_SET('#{value}', #{column}) <> 0"),
+
+    /**
+     * PostgreSQL
+     *
+     * 华为 openGauss 使用 ProductName 与 PostgreSQL 相同
+     */
+    POSTGRE_SQL(DbType.POSTGRE_SQL,"PostgreSQL", "POSITION('#{value}' IN #{column}) <> 0"),
+
+    /**
+     * SQL Server
+     */
+    SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"),
+
+    /**
+     * 达梦
+     */
+    DM(DbType.DM, "DM DBMS", "FIND_IN_SET('#{value}', #{column}) <> 0"),
+
+    /**
+     * 人大金仓
+     */
+    KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION('#{value}' IN #{column}) <> 0"),
+    ;
+
+    public static final Map<String, DbTypeEnum> MAP_BY_NAME = Arrays.stream(values())
+            .collect(Collectors.toMap(DbTypeEnum::getProductName, Function.identity()));
+
+    public static final Map<DbType, DbTypeEnum> MAP_BY_MP = Arrays.stream(values())
+            .collect(Collectors.toMap(DbTypeEnum::getMpDbType, Function.identity()));
+
+    /**
+     * MyBatis Plus 类型
+     */
+    private final DbType mpDbType;
+    /**
+     * 数据库产品名
+     */
+    private final String productName;
+    /**
+     * SQL FIND_IN_SET 模板
+     */
+    private final String findInSetTemplate;
+
+    public static DbType find(String databaseProductName) {
+        if (StrUtil.isBlank(databaseProductName)) {
+            return null;
+        }
+        return MAP_BY_NAME.get(databaseProductName).getMpDbType();
+    }
+
+    public static String getFindInSetTemplate(DbType dbType) {
+        return Optional.of(MAP_BY_MP.get(dbType).getFindInSetTemplate())
+                .orElseThrow(() -> new IllegalArgumentException("FIND_IN_SET not supported"));
+    }
+}

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

@@ -1,9 +1,14 @@
 package cn.iocoder.yudao.framework.mybatis.core.util;
 
+import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
+import cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum;
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
 import com.baomidou.mybatisplus.annotation.DbType;
 
+import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.DriverManager;
+import java.sql.SQLException;
 
 /**
  * JDBC 工具类
@@ -35,8 +40,22 @@ public class JdbcUtils {
      * @return DB 类型
      */
     public static DbType getDbType(String url) {
-        String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null);
-        return DbType.getDbType(name);
+        return com.baomidou.mybatisplus.extension.toolkit.JdbcUtils.getDbType(url);
+    }
+
+    /**
+     * 通过当前数据库连接获得对应的 DB 类型
+     *
+     * @return DB 类型
+     */
+    public static DbType getDbType() {
+        DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtils.getBean(DynamicRoutingDataSource.class);
+        DataSource dataSource = dynamicRoutingDataSource.determineDataSource();
+        try (Connection conn = dataSource.getConnection()) {
+            return DbTypeEnum.find(conn.getMetaData().getDatabaseProductName());
+        } catch (SQLException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
     }
 
 }

+ 20 - 2
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java

@@ -1,8 +1,11 @@
 package cn.iocoder.yudao.framework.mybatis.core.util;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.SortingField;
+import cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum;
+import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
@@ -34,7 +37,7 @@ public class MyBatisUtils {
         // 排序字段
         if (!CollectionUtil.isEmpty(sortingFields)) {
             page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
-                    OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
+                            OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
                     .collect(Collectors.toList()));
         }
         return page;
@@ -56,7 +59,7 @@ public class MyBatisUtils {
 
     /**
      * 获得 Table 对应的表名
-     *
+     * <p>
      * 兼容 MySQL 转义表名 `t_xxx`
      *
      * @param table 表
@@ -85,4 +88,19 @@ public class MyBatisUtils {
         return new Column(tableName + StringPool.DOT + column);
     }
 
+    /**
+     * 跨数据库的 find_in_set 实现
+     *
+     * @param column 字段名称
+     * @param value  查询值(不带单引号)
+     * @return sql
+     */
+    public static String findInSet(String column, Object value) {
+        // 这里不用SqlConstants.DB_TYPE,因为它是使用 primary 数据源的 url 推断出来的类型
+        DbType dbType = JdbcUtils.getDbType();
+        return DbTypeEnum.getFindInSetTemplate(dbType)
+                .replace("#{column}", column)
+                .replace("#{value}", StrUtil.toString(value));
+    }
+
 }

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

@@ -52,13 +52,16 @@ spring:
           #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
           #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
           #          url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
-          #          url: jdbc:postgresql://127.0.0.1:54321/test  # KingbaseES 人大金仓 连接的示例, MyBatis不兼容官方驱动
+          #          url: jdbc:kingbase8://127.0.0.1:54321/test  # 人大金仓 KingbaseES 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例
           username: root
           password: 123456
-        #          username: sa # SQL Server 连接的示例
-        #          password: Yudao@2024 # SQL Server 连接的示例
-        #          username: SYSDBA # DM 连接的示例
-        #          password: SYSDBA001 # DM 连接的示例
+          #          username: sa # SQL Server 连接的示例
+          #          password: Yudao@2024 # SQL Server 连接的示例
+          #          username: SYSDBA # DM 连接的示例
+          #          password: SYSDBA001 # DM 连接的示例
+          #          username: root # OpenGauss 连接的示例
+          #          password: Yudao@2024 # OpenGauss 连接的示例
         slave: # 模拟从库,可根据自己需要修改
           lazy: true # 开启懒加载,保证启动速度
           url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true