RuoYi 项目适配达梦数据库(二)

   续接上篇。现在在做的项目使用了 RuoYi 框架(V4.7.5,前后端不分离版),需要适配达梦数据库。上篇完成了系统(代码生成模块除外)的数据库适配,基本运行正常;今天刚好把代码生成模块的适配也修改完成了,记录下来,好把上篇遗留的坑填了。


一、适配任务概述

  1. 代码生成模块的适配主要涉及到定义表的数据读取和表中列信息的读取,需要找到表和列信息在数据库里的存放位置及依赖关系;
  2. 可能还会涉及到生成的 Mapper 中,sql 语句需调整的问题,即修改模板文件的问题。不过鉴于生成的代码主要是 CRUD 的简单代码,涉及到专有 sql 函数的可能性不大。因此,这里仅作提醒,未做验证。需随着后期项目的展开,碰到问题再进行处理。

二、Mapper 代码修改

  1. 主要涉及两部分:表信息读取 GenTableMapper,列信息读取 GenTableColumnMapper;

  2. 达梦数据库中查询表和列定义信息的简要说明:
    a) SYS.SYSOBJECTS,记录系统中所有对象的信息,表定义信息从这里获取;
    b) SYS.SYSTABLECOMMENTS,记录表或视图的注释信息;
    c) SYS.SYSCOLUMNS,记录列定义信息;
    d) SYS.SYSCOLUMNCOMMENTS,记录列的注释信息;
    e) 获取当前模式名称:select CURR_SCH from V$SESSIONS LIMIT 1;
    f) 可在安装目录里查看相应文档:DM 系统管理员手册 -> 附录 1- 数据字典;

  3. 获取表定义信息,修改 GenTableMapper
    a) 方法 selectDbTableList 修改:

    • 原代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
    select table_name, table_comment, create_time, update_time from information_schema.tables
    where table_schema = (select database())
    AND table_name NOT LIKE 'QRTZ_%' AND table_name NOT LIKE 'gen_%'
    AND table_name NOT IN (select table_name from gen_table)
    <if test="tableName != null and tableName != ''">
    AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
    </if>
    <if test="tableComment != null and tableComment != ''">
    AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
    </if>
    order by create_time desc
    </select>
    • 修改后代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
    select so.NAME table_name, st.COMMENT$ table_comment, so.CRTDATE create_time from SYS.SYSOBJECTS so left join SYS.SYSTABLECOMMENTS st on so.NAME = st.TVNAME and st.TABLE_TYPE = 'TABLE' and st.SCHNAME = (select CURR_SCH from V$SESSIONS LIMIT 1)
    where so.TYPE$='SCHOBJ' and so.SUBTYPE$='UTAB' and so.PID = -1 and INSTR(so.NAME,'##') = 0 and so.SCHID = (select ID from SYS.SYSOBJECTS where TYPE$='SCH' and NAME= (select CURR_SCH from V$SESSIONS LIMIT 1)) and so.NAME NOT LIKE 'QRTZ_%' AND so.NAME NOT LIKE 'gen_%'
    and so.NAME NOT IN (select table_name from gen_table)
    <if test="tableName != null and tableName != ''">
    and lower(so.NAME) like lower(concat('%', #{tableName}, '%'))
    </if>
    <if test="tableComment != null and tableComment != ''">
    and lower(st.COMMENT$) like lower(concat('%', #{tableComment}, '%'))
    </if>
    order by so.CRTDATE desc
    </select>

    b) 方法 selectDbTableListByNames 修改:

    • 原代码:
    1
    2
    3
    4
    5
    6
    7
    8
    <select id="selectDbTableListByNames" resultMap="GenTableResult">
    select table_name, table_comment, create_time, update_time from information_schema.tables
    where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
    and table_name in
    <foreach collection="array" item="name" open="(" separator="," close=")">
    #{name}
    </foreach>
    </select>
    • 修改后代码:
    1
    2
    3
    4
    5
    6
    7
    8
    <select id="selectDbTableListByNames" resultMap="GenTableResult">
    select so.NAME table_name, st.COMMENT$ table_comment, so.CRTDATE create_time from SYS.SYSOBJECTS so left join SYS.SYSTABLECOMMENTS st on so.NAME = st.TVNAME and st.TABLE_TYPE = 'TABLE' and st.SCHNAME = (select CURR_SCH from V$SESSIONS LIMIT 1)
    where so.TYPE$='SCHOBJ' and so.SUBTYPE$='UTAB' and so.PID = -1 and INSTR(so.NAME,'##') = 0 and so.SCHID = (select ID from SYS.SYSOBJECTS where TYPE$='SCH' and NAME= (select CURR_SCH from V$SESSIONS LIMIT 1)) and so.NAME NOT LIKE 'qrtz_%' and so.NAME NOT LIKE 'gen_%'
    and so.NAME in
    <foreach collection="array" item="name" open="(" separator="," close=")">
    #{name}
    </foreach>
    </select>
  4. 获取列定义信息,修改 GenTableColumnMapper
    a) 方法 selectDbTableColumnsByName 修改

    • 原代码:
    1
    2
    3
    4
    5
    <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
    select column_name, (case when (is_nullable = 'no' <![CDATA[ &&]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk,ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
    from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})
    order by ordinal_position
    </select>
    • 修改后代码:
    1
    2
    3
    4
    5
    <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
    select sc.NAME column_name, (case when sc.INFO2 = 1 then '1' else '0' end) is_pk, sc.COLID sort, scc.COMMENT$ column_comment, (case when sc.INFO2 = 1 then '1' else '0' end) as is_increment, LOWER(sc.TYPE$) column_type
    from SYS.SYSCOLUMNS sc left join SYS.SYSOBJECTS so on sc.ID = so.ID left join SYS.SYSCOLUMNCOMMENTS SCC on sc.NAME = scc.COLNAME and scc.TABLE_TYPE = 'TABLE' and scc.SCHNAME = (select CURR_SCH from V$SESSIONS LIMIT 1) and scc.TVNAME= so.NAME
    where so.TYPE$='SCHOBJ' and so.SUBTYPE$='UTAB' and so.PID = -1 and so.SCHID = (select ID from SYS.SYSOBJECTS where TYPE$='SCH' and NAME= (select CURR_SCH from V$SESSIONS LIMIT 1)) and so.NAME = #{tableName}
    </select>
    • 修改后代码(2023/5/5 更新):
    1
    2
    3
    4
    5
    6
    7
    <!-- RuoYi-vue 3.8.5 版本,主要增加了(is_required 是否必填属性) -->
    <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
    select sc.NAME column_name, (case when sc.NULLABLE$ = 'N' then '1' else null end) as is_required, (case when sc.INFO2 = 1 then '1' else '0' end) is_pk, sc.COLID sort, scc.COMMENT$ column_comment, (case when sc.INFO2 = 1 then '1' else '0' end) as is_increment, LOWER(sc.TYPE$) column_type
    from SYS.SYSCOLUMNS sc left join SYS.SYSOBJECTS so on sc.ID = so.ID left join SYS.SYSCOLUMNCOMMENTS SCC on sc.NAME = scc.COLNAME and scc.TABLE_TYPE = 'TABLE' and scc.SCHNAME = (select CURR_SCH from V$SESSIONS LIMIT 1) and scc.TVNAME= so.NAME
    where so.TYPE$='SCHOBJ' and so.SUBTYPE$='UTAB' and so.PID = -1 and so.SCHID = (select ID from SYS.SYSOBJECTS where TYPE$='SCH' and NAME= (select CURR_SCH from V$SESSIONS LIMIT 1)) and so.NAME = #{tableName}
    order by sc.COLID
    </select>

三、可能出现的问题

  1. 导入表的列的 Java 类型错误:
    a)如果全部列的类型都转换成了 String,极可能是因为上面的 selectDbTableColumnsByName 语句中读取类型时未转换小写 LOWER(sc.TYPE$)。因 Mysql 里读到的类型是全小写,所以框架里是以小写类型匹配的;
    b)如果是数字类型都转换成了 Long,是因为原逻辑是以类型后面的长度和精度来判断的,但是我们在读取达梦的类型时,因为类型和长度、精度是分开的,未拼接,所以全给了默认值 Long;
    c)其实改的思路就两个:一种是将 sql 查询结果和 mysql 的统一,即类型拼接长度和精度;另一种是改匹配逻辑,按类型名称匹配;
    d)这里我选择的是第二种,因为修改逻辑代码更灵活一点;主要涉及两个类,com.zw.common.constant.GenConstantscom.zw.generator.util.GenUtils,代码片段如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 增加两个常量,用于判断浮点数和整型
    public class GenConstants{
    /***************** 省略 *********************/
    /** 浮点数 */
    public static final String[] COLUMNTYPE_NUMBER_DOUBLE = { "number", "float", "double", "decimal" };

    /** 整型 */
    public static final String[] COLUMNTYPE_NUMBER_INTEGER = { "tinyint", "smallint", "mediumint", "int", "integer",
    "bit" };
    /***************** 省略 *********************/
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public class GenUtils{
    /***************** 省略 *********************/
    /**
    * 初始化列属性字段
    */
    public static void initColumnField(GenTableColumn column, GenTable table){
    /***************** 省略 *********************/
    else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType))
    {
    column.setHtmlType(GenConstants.HTML_INPUT);

    /* 原代码
    // 如果是浮点型 统一用 BigDecimal
    String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
    if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
    {
    column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
    }
    // 如果是整形
    else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10)
    {
    column.setJavaType(GenConstants.TYPE_INTEGER);
    }
    // 长整形
    else
    {
    column.setJavaType(GenConstants.TYPE_LONG);
    }*/

    /** 修改后的代码 */
    // 如果是浮点型 统一用 Double
    if (arraysContains(GenConstants.COLUMNTYPE_NUMBER_DOUBLE, dataType))
    {
    column.setJavaType(GenConstants.TYPE_DOUBLE);
    }
    // 如果是整形
    else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER_INTEGER, dataType))
    {
    column.setJavaType(GenConstants.TYPE_INTEGER);
    }
    // 长整形
    else
    {
    column.setJavaType(GenConstants.TYPE_LONG);
    }
    }
    /***************** 省略 *********************/
    }
    /***************** 省略 *********************/
    }

四、后记

  1. 经过上述步骤,经简单测试可正常运行,代码生成模块的适配工作完成;
  2. 至此,完成了整个 RuoYi 项目的达梦数据库适配;
  3. 本次项目适配,应该对 Vue 版和 Cloud 版的达梦数据库适配有一定借鉴作用。