从mysql 到kingbase(人大金仓)

一、背景介绍

公司主要做toG 的项目,经常服务于各个地方政府。之前某省会城市下属区县项目,需要将已交付的项目中所使用到的各类中间件全部替换为国产,其中就包括了数据库的替换。项目中一直使用的是mysql数据库,后经商务调研及选型,决定替换为国产数据库 kingBase (人大金仓数据库),并由我负责所有应用的代码迁移工作。

我们的项目采用的是springCloud微服务体系,数据访问层用的是mybatisPlus, 微服务的个数比较多,我需要一个一个迁移并验证,这其实确实是一个比较繁琐且漫长的工作,并且由于只搭建了一套环境的数据库,我没法本地连接调试,每次都只能发包后通过观察错误日志的方式进行排查,给我的迁移工作增加了更大的困难。

二、适配过程

这里先强调一下,我的主要工作是把当前使用的mysql数据库,切换到人大金仓数据库上,这是一个迁移的过程,而不是说一个新项目从头开始就使用人大金仓,所以我所关注的主要就是mysql和人大金仓的兼容性上,把一些人大金仓无法成功的执行的mysql 中的语句改成能让他执行的语句。

而适配的第一步,其实主要是人大金仓的技术人员和我方的运维人员的工作。 首先由人大金仓的技术人员在我方指定的服务器上,帮助我们安装人大金仓的数据库,并且提供人大金仓(下面都使用kingbase替代)的数据库连接工具和相关的技术文档。并由我方运维人员根据他们提供的数据库迁移手册,将mysql上的数据迁移到kingbase中。完成迁移后,会给我一个kingbase数据库的连接相关信息(url,username,password等)和一份迁移文档。

接下来我就要根据文档进行应用上数据库迁移了,这里所说的迁移,其实就是做一个替换,将代码里的mysql连接替换成kingbase连接。替换步骤如下:

  1. 更换驱动

    对方技术人员提供了一个驱动jar包,kingbase8-x.x.x.jar ,首先第一步我先将这个jar包上传到了公司私服上,然后用坐标的方式将其配置到了项目的pom文件中,同时注释掉里面的mysql驱动。

  2. 修改数据库连接配置信息

    由于我们使用的是nacos配置中心,直接在nacos里进行修改:

  1. spring:
  2. datasource:
  3. driver-class-name: com.kingbase8.Driver # 配置MySQL8的驱动程序类
  4. url: jdbc:kingbase8:/host:port/database?para1=val1...
  5. username: root # 数据库用户名
  6. password: root # 数据库连接密码

      3. 重新启动服务,测试,解决报错。

上面这几步就是迁移的主要步骤了,如果运气比较好,完成上面的改动后,服务应该会正常启动,但是也有可能会出现一些问题,接下来就是要具体问题具体分析了,报的错误基本上都是sql不兼容的一些问题。然后就要根据具体的报错情况进行sql的适配。同时人大金仓的技术人员也会组建一个答疑的群,遇到一些报错可以发到答疑群中,他们也会有专业的技术人员进行解答。

三、常见问题解决

问题一: Error updateting database. Cause: com.kingbase8.util.KSQLException: ERROR: syntax error at or near “`”

这个问题是由于kingbase数据库不支持反引号造成的,有一些框架在生成一些增删改查语句的时候,可能习惯将字段名称使用反引号包裹起来,我们需要将反引号去掉或者改为双引号引号重新启动项目后解决。

类似于:

从mysql 到kingbase(人大金仓)

问题二: DATE_ADD函数不支持

sql中有使用DATE_ADD函数的地方报错,原因是默认的kingbase中是没有DATE_ADD函数的,需要自己创建(非系统函数,每个库都需要创建): 创建代码如下:

  1. create or replace function date_add(v_date text , v_interval interval)
  2. returns text as $$
  3. declare
  4. v_rt text;
  5. begin
  6. select to_char(v_date::timestamp(0) + v_interval,'yyyy-mm-dd hh24:mi:ss' ) into v_rt;
  7. if length(v_date) = 10 and v_rt like '% 00:00:00' then
  8. select substr(v_rt,0,10) into v_rt;
  9. end if;
  10. return v_rt;
  11. end;
  12. $$
  13. LANGUAGE plpgsql;

问题三: DATE_FORMAT 函数不支持

解决方案: 需要自己创建(非系统函数,每个库都需要创建),创建语句如下:

  1. create or replace function date_format(para1 timestamp,para2 text) returns text
  2. as $$
  3. declare
  4. form1 text;
  5. begin
  6. --form1=replace(para2,'%M','Month');
  7. form1=replace(para2,'%W','Day');
  8. --form1=replace(form1,'%D','DDth');
  9. form1=replace(form1,'%Y','YYYY');
  10. --form1=replace(form1,'%y','yy');
  11. form1=replace(form1,'%a','Dy');
  12. form1=replace(form1,'%d','DD');
  13. form1=replace(form1,'%e','DD');
  14. form1=replace(form1,'%m','MM');
  15. form1=replace(form1,'%c','MM');
  16. form1=replace(form1,'%b','Mon');
  17. form1=replace(form1,'%j','DDD');
  18. form1=replace(form1,'%H','HH24');
  19. form1=replace(form1,'%k','HH24');
  20. --form1=replace(form1,'%h','HH');
  21. --form1=replace(form1,'%I','HH');
  22. form1=replace(form1,'%l','HH');
  23. form1=replace(form1,'%i','MI');
  24. form1=replace(form1,'%r','HH:MI:SS');
  25. form1=replace(form1,'%T','HH24:MI:SS');
  26. --form1=replace(form1,'%S','SS');
  27. form1=replace(form1,'%s','SS');
  28. form1=replace(form1,'%%','%');
  29. return to_char(para1,form1);
  30. end;
  31. $$ LANGUAGE plpgsql;

问题四: Cause: com.github.pagehelper.PageException: 无法自动获取数据库类型,请通过 helperDialect 参数指定!

解决方案: 在配置文件中手动指定:

  1. pagehelper:
  2. helperDialect: postgresql
  3. auto-runtime-dialect: false #默认参数,由于我的项目本地写死了true,我这里需要强制覆盖一下,如果配置成true,上面的配置不生效

问题五: ORDER BY isnull(kpi_seq), kpi_seq ASC ,create_time desc 报错

解决方案: kingbase不支持 isnull(字段名)的写法,需要改为一下写法:

ORDER BY kpi_seq isnull, kpi_seq ASC ,create_time desc

问题六: ERROR: CONNECT BY clause required in this query block

解决方案: 这个问题,主要是我在sql中使用了类似于name, level这样的关键字,kingbase中对于关键字的要求还是比较多的,有一些关键字是不允许我们在sql中使用的。解决方案有两种,一是把这些关键字的字段名修改一下。二是设置关键字的忽略。具体关键字有哪些,可以参考kingbase提供的文档,里面有详细参数。关键字忽略的方式,执行如下sql:

  1. alter system set exclude_reserved_words = 'level';
  2. select sys_reload_conf();

问题七: druid连接池在连接kingbase时会产生dbType not support, 或者 无法识别数据库类型的错误。

解决方案: 由于druild中一般会自动根据jdbc-url自动检查数据库的类型,但是它里面只有检查url中是否包含kingbase, 但是却不认识kingbase8,就会出现这个问题,我们需要自己设置driverClassName,同时druild的filter中要把wall去掉,因为kingbase不支持这个参数。

  1. druidDataSource.setFilters("stat,slf4j");
  2. druidDataSource.setDriverClassName("com.kingbase8.Driver")

问题八: com.kingbase8.util.KSQLException: Cannot convert the column of type TINYINT to requested type boolean.

解决方案: 这个问题主要是由于mysql中没有boolean类型,所以一般都使用tinyint来存储标志位,然后在实体中使用boolean类型来接收,类似mybatis或者mybatisPlus框架都会自动把tinyint类型转换为boolean类型。但是在kingbase里就不行了,解决方案: 这里只针对mybatisPlus,

首先实体类上的TableName注解中加入一个 autoResultMap = true

@TableName(value = “d_lc_res”, autoResultMap = true)

然后在boolean类型的字段注解上加上一个TypeHandler

  1. @TableField(typeHandler = TinyIntToBooleanHandler.class, jdbcType= JdbcType.TINYINT)
  2. private Boolean warningFlag;

TinyIntToBooleanHandler:

  1. package com.xxx.global.config.mp;
  2. import org.apache.ibatis.type.BaseTypeHandler;
  3. import org.apache.ibatis.type.JdbcType;
  4. import org.apache.ibatis.type.MappedJdbcTypes;
  5. import org.apache.ibatis.type.MappedTypes;
  6. import java.sql.CallableStatement;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. /**
  11. * @className: TinyIntToBooleanHandler
  12. * @description:
  13. * @author liushuai
  14. * @date 2022/9/19 3:21 PM
  15. */
  16. @MappedTypes({Boolean.class})
  17. @MappedJdbcTypes({JdbcType.TINYINT})
  18. public class TinyIntToBooleanHandler extends BaseTypeHandler<Boolean> {
  19. @Override
  20. public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType) throws SQLException {
  21. ps.setInt(i,Boolean.TRUE.equals(parameter) ? 1 : 0 );
  22. }
  23. @Override
  24. public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException {
  25. int anInt = rs.getInt(columnName);
  26. return Integer.valueOf("1").equals(anInt);
  27. }
  28. @Override
  29. public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  30. int anInt = rs.getInt(columnIndex);
  31. return Integer.valueOf("1").equals(anInt);
  32. }
  33. @Override
  34. public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  35. int anInt = cs.getInt(columnIndex);
  36. return Integer.valueOf("1").equals(anInt);
  37. }
  38. }

这样就可以了,这里要注意,这种方式只对mybatis已经封装好的方法有效,比如list, getOne,等,如果是我们自己写的sql, 还需要做进一步的处理:

对于自己写的sql, 我们需要使用resultMap来接收, 同时在定义resultMap的时候,给相应字段添加typeHandler属性:

  1. <resultMap id="kpiDO" type="com.xxx.entity.KpiDO">
  2. <result column="warningFlag" property="warningFlag" typeHandler="com.cestc.global.config.mp.TinyIntToBooleanHandler" />
  3. // 省略其他字段
  4. </resultMap>

四、总结

上面就是最近一段时间在迁移国产数据库过程中的一些步骤和解决问题的方式,希望能够给大家带来帮助。整体来说迁移的过程还是比较痛苦的,上面只列出了常规应用的一些问题,还有一些使用动态数据源的应用,迁移起来的问题其实更大,像一些查询所有库,查询所有表,查询所有字段的sql写法和mysql都是完全不一样的。

虽然过程很艰辛,但是我想说的是,在大形势不断紧张的情况下,还是支持有更多企业、机构甚至个人,可以更多的支持国产软件的发展,有朝一日让我们的国产软件也能够站在世界的顶端,我们软件人不在遭受其他人的制裁与威胁。希望这一天早日到来,这也是全体中国软件人真正站起来的时候。

发布者:小站,转转请注明出处:http://blog.gzcity.top/4914.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年7月24日 16:31
下一篇 2022年5月4日 01:16

相关推荐

  • Git clone代码报“fatal: Out of memory, malloc failed (tried to allocate xxxx bytes)”的解决办法

    问题描述: 之前clone代码的时候一直没问题,今天clone代码的时候突然报错“fatal: Out of memory, malloc failed (tried to allocate 1058576000 bytes)”。 按报错的理解就是本机的内存不足了,查看了本机运行的服务和进程,没有可以停掉的服务和进程,那该怎么办呢? 查询资料都说的是因为GI…

    2023年3月21日
    1.2K1770
  • OOM专题-String 和 StringBuilder的最大可用长度是多少

    String 直接量 超出 65535之后(127KB),IDEA 编译器会报错,因为 直接量是存储在常量池,常量池的定义规定中 有长度限制 2e16 。 String在运行期间的拼接后的最大长度是 int 的最大值 。 StringBuilder 长度是 int 的最大值 2e32 ,21亿 个字符,42亿字节 Byte= 大小4G。 超出长度的需要 用文…

    Java 2022年7月5日
    244100
  • Spring Cloud Alibaba版本对应关系

    版本依赖关系(推荐使用) 由于 Spring Boot 2.4+ 和以下版本之间变化较大,目前企业级客户老项目相关 Spring Boot 版本仍停留在 Spring Boot 2.4 以下,为了同时满足存量用户和新用户不同需求,社区以 Spring Boot 2.4 为分界线,同时维护 2.2.x 和 2021.x 两个分支迭代。 2021.x 分支 适配…

    Java 2022年6月28日
    11.7K30670
  • Android版本 (1.0~12.0) 与API Level (SDK版本1~32) 对应表

    什么是 API 级别? API 级别是一个对 Android 平台版本提供的框架 API 修订版进行唯一标识的整数值。 Android 平台提供了一种框架 API,应用可利用它与底层 Android 系统进行交互。 该框架 API 由以下部分组成: 一组核心软件包和类 一组用于声明清单文件的 XML 元素和属性 一组用于声明和访问资源的 XML 元素和属性 …

    Java 2023年6月1日
    17.5K24930
  • 反编译获取微信小程序源码(包含错误解决办法)

    本文章仅用于分享自己反编译的过程以及解决办法,切勿小程序反编译成功后做一些违法事情! 一. 前言 微信小程序的反编译听起来很屌,其实非常简单,就是纯粹的傻瓜式的操作。GitHub有写好的node.js脚本!要想拿到微信小程序源码,找到源文件在手机存放的位置就行,源文件拿到,用反编译脚本跑一下,微信小程序代码包里的所有文件、资源就出来了。 二. 微信小程序的小…

    学习笔记 2022年11月15日
    849380