达梦 | 记一次国产数据库适配的思考过程

2022-03-30 00:00:00 数据库 分页 环境 获取 方言

简介:达梦数据库管理系统是由达梦公司推出的具有完全自主知识产权的高性能数据库管理系统,简称DM。达梦为中国电子信息产业集团(CEC)旗下基础软件企业,其可靠性、高性能、海量数据处理和安全性做了大量的研发和改进工作,极大提升了达梦数据库产品的性能、可靠性、可扩展性,能同时兼顾OLTP和OLAP请求。






背景:在一些核心技术领域,尤其是针对其中很多重要组成部分的突破,国产化都起着至关重要的作用。在计算机-软件开发行业中,现在很多成熟的技术框架,数据库也都来自于国外。早期,依赖于核心技术的引进,在引进的基础上做上层应用,进而不断迭代。而现在核心技术自研,数据库自研等成了技术攻关的新浪潮。阿里曾一直提出“去IOE”的概念-其中IBM是服务器提供商,Oracle是数据库软件提供商,EMC则是存储设备提供商。



思考:
1、当使用K8、Docker容器化编排技术受到限制,当Oracle、MySql数据存储等数据库软件不再向我们提供正常的服务?
2、在我们的项目工程中,若是没有了这些核心技术提供正常的服务,如何能够去及时地采取补救的措施,使得业务能够平滑过渡,做到让用户无感知体验?
3、是否能够拥有自己的数据存储解决方案,技术框架是否能够适配,能否做到更好地兼容?



场景:4月伊始,原本的月度aim计划似乎有些较大的冲击,就是临时接到一个极其重要的任务,就是对达梦的预研,国产数据库在现有渠道产品上的适配,推进项目工程落地的flag。



当接到任务之后,脑子里马上浮现的是数学带来化解般的灵感。于是对任务做进一步的拆解,将复杂步骤简单化,从大化小,从小化无。这种化解的思维逻辑,在一定程度上,的确有很大的帮助意义,这不仅可以让自己不至于陷入不知从何处开始-懵懂无措-delay的状态-特别是对于国产自研核心技术这一块,网上能找到可用的资料少之又少。同时,还可以让自己的思维逻辑更清晰,更能够明确需要做什么,能够做到什么程度-粒度细化。于是对手头上现有的工作,根据实际情况分配优先级,重新调整好排期,Starting...
下文,小编主要以达梦-DM适配为例->常见问答Q-A的方式阐述:



Q

DM达梦数据库安装,其对应版本及驱动如何选取,适配程度更佳?



A
DM8+DmJdbcDriver18
这里提供百度云的方式供使用者下载并安装:   
https://pan.baidu.com/s/1nFBEnX3lYqIjBZo8ZPADgg
一、下载&安装
首先,需要说明的是达梦的安装包放一起的,没有像Oracle那样把客户端独立出来,跟windows的安装包是一样大的,大约1.1GB的样子,好在的一点就是,直接解压就可以使用,在云盘中附详细操作指南

解压以后,里面有一个\source\tool\manager.exe的应用程序,可直接打开,登录就行。

  其中,达梦8 JDBC驱动版本说明

  1. DmJdbcDriver16 对应 Jdk1.6 及以上环境

  2. DmJdbcDriver17 对应 Jdk1.7 及以上环境

  3. DmJdbcDriver18 对应 Jdk1.8 及以上环境

  达梦8 hibernate方言包对应版本说明:

  1. DmDialect-for-hibernate2.0.jar  对应 Jdk1.4及以上, hibernate2.0 环境。

  2. DmDialect-for-hibernate2.1.jar  对应 Jdk1.4及以上, hibernate2.1 2.X 环境。

  3. DmDialect-for-hibernate3.0.jar  对应 Jdk1.4及以上, hibernate3.0 环境。

  4. DmDialect-for-hibernate3.1.jar  对应 Jdk1.4及以上, hibernate3.1 3.5 环境。

  5. DmDialect-for-hibernate3.6.jar  对应 Jdk1.5及以上, hibernate3.6 3.X 环境。

  6. DmDialect-for-hibernate4.0.jar  对应 Jdk1.6及以上, hibernate4.0 4.X 环境。

  7. DmDialect-for-hibernate5.0.jar  对应 Jdk1.7及以上, hibernate5.0 5.2 环境。

  8. DmDialect-for-hibernate5.3.jar 对应 Jdk1.7及以上, hibernate5.3 5.4 环境。

  说明:以上的hibernate版本指的是Hibernate ORM版本,注意区分hibernate search版本。


   Hibernate.cfg.xml配置说明


   i、驱动名称

<property name="connection.driver_class">dm.jdbc.driver.DmDriver</property>

   ii、方言包名称

<property name="dialect">org.hibernate.dialect.DmDialect</property>

‍‍‍‍‍‍‍‍‍‍‍‍‍‍

  其他jar包说明:

  1. DmDictionary.jar    openjpa1.2方言包,对应 Jdk1.6及以上环境。

  2. dmjooq-dialect-3.12.3.jar     jooq方言包,对应 Jdk1.8及以上环境。

  3. dm8-oracle-jdbc16-wrapper.jar    oracle 到达梦的JDBC驱动桥接,应用中如果使用了非标准的oracle JDBC特有的对象,无需修改应用代码,可以桥接到达梦的JDBC连接达梦数据库,对应 Jdk1.6及以上环境。

  4. DmHibernateSpatial-1.0.jar hibernatespatial方言包,对应hibernate spatial 1.0 环境,对应 Jdk1.5及以上环境。

  5. DmHibernateSpatial-1.1.jar hibernatespatial方言包,对应hibernate spatial 1.1环境,对应 Jdk1.5及以上环境。

  6. gt-dameng-2.8.jar GeoServer 2.8环境方言包,对应 Jdk1.6及以上环境。

  7. gt-dameng-2.11.jar GeoServer 2.11环境方言包,对应 Jdk1.8及以上环境。

  8. gt-dameng-2.15.jar GeoServer 2.15环境方言包,对应 Jdk1.8及以上环境。

  二、DM8&Client

  接下来,打开DM8客户端,可通过dm sql脚本方式去创建表,这里只是简单创建了一张crm_version表。这里尤其需要注意的是创建表名不需要带双引号,达梦默认是大写,sql方言中也不需要额外处理,若是通过DM8工具去建表建字段或者带小写加双引号创建脚本,出现双引号则在实际的sql方言中也需要加上双引号,否则执行sql会抛出视图或表不存在,字段列名不存在的异常。

   若是通过Mysql或Oracle或其他数据库,文件等方式迁移导入。这里记录一下迁移过程中遇到的问题,在迁移的时候,报某些字段超长。于是,查看了MySql中那些字段的类型及长度,都是varchar(50) 。这里应该是迁移有些字段,须在DM数据库中增加位宽,在MySql中varchar是表示字符,varchar(50)表示可以存放50个字符,但是DM的默认跟Oracle是一样的,varchar(50)表示50个字节。这就意味着,50个字节,如果存中文,在utf-8的字符集下,只能存多16个。所以,如果MySql库到DM,varchar类型,需特别留意一下。

  三、DM8&DmJdbcDriver

  然后,在项目工程中引入达梦数据库驱动,SpringBoot对MySql做了集成,没有get到对达梦数据库做集成,小编这里采用的jdk1.8,安装的达梦数据库也是DM8,所以这里引入:

DmJdbcDriver18,其相对于DmJdbcDriver17作出了很大的改进

  i、本地引入的方式

  在pom.xml文件中,引入依赖jar

<!-- 达梦数据库驱动 --><dependency>      <groupId>com.dm</groupId>       <artifactId>DmJdbcDriver18</artifactId>        <version>1.8</version>         <scope>system</scope>          <systemPath>${project.basedir}/src/main/resources/lib/DmJdbcDriver18.jar</systemPath></dependency>

  ii、nexus私服引入的方式

  在pom.xml文件中,引入依赖jar

<!-- 达梦数据库驱动 --><dependency>      <groupId>com.dm</groupId>       <artifactId>DmJdbcDriver18</artifactId>        <version>1.8</version></dependency>

  说明:这里的groupId坐标参数,可由使用者自行在nexus中upload创建声明,然后在pom.xml中引入相关坐标即可。

  扩展:若是需要从本地deploy到nexus或是先获取本地仓库.m2的包->nexus仓库的包->aliyun maven仓库的包,为了解决开发过程中jar包拉取异常等问题,则在maven中settings.xml中可这样去配置:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">       <!-- 本地仓库 -->       <localRepository>D:/.m2</localRepository>       <mirrors>           <mirror>               <id>nexus</id>               <mirrorOf>*</mirrorOf>               <name>yd nexus</name>               <url>http://ip:port/repository/maven-public/</url>           </mirror>       </mirrors>   <mirror>    <id>alimaven</id>    <mirrorOf>central</mirrorOf>    <name>aliyun maven</name>    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>   </mirror>      <profiles>           <profile>               <id>nexus</id>               <repositories>                   <repository>                       <id>maven-snapshots</id>                       <url>http://ip:port/repository/maven-snapshots/</url>                       <releases><enabled>false</enabled></releases>                               <snapshots><enabled>true</enabled></snapshots>                   </repository>                   <repository>                       <id>maven-releases</id>                       <url>http://ip:port/repository/maven-releases/</url>                       <releases><enabled>true</enabled></releases>                               <snapshots><enabled>false</enabled></snapshots>                   </repository>               </repositories>           </profile>       </profiles>       <servers>      <server>         <id>nexus</id>         <username>yxd179</username>         <password>yxd179</password>       </server>       <server>         <id>maven-releases</id>         <username>yxd179</username>         <password>yxd179</password>       </server>       <server>         <id>maven-snapshots</id>         <username>yxd179</username>         <password>yxd179</password>       </server>     </servers>     <activeProfiles>       <activeProfile>nexus</activeProfile>     </activeProfiles>    </settings>

  四、配置达梦数据库的信息

  说明:这里采取Nacos注册中心去管理项目工程中用到的一些配置信息,Nacos的介绍具体可参考小编上篇公众号:

https://mp.weixin.qq.com/s/UyQ6wHGMyrGnDRwNZaJz0Q

  至此,达梦数据库环境安装,相关版本及其依赖的选取跟引入,配置信息完毕。



Q

在DM8达梦数据库适配的项目工程中,多数据库兼容+数据库连接池+ORM映射框架+分页相关控件?



A
HiKari+MyBatis3.4.6+tk.MyBatis+PageHelper
hikari:数据库连接池-Hikari3.4.2。
  mybatis:ORM(对象关系映射)框架,内部封装了 JDBC,不需要花费精力处理加载驱动、创建连接、创建 statement 等繁杂过程。
// 驱动-连接地址-账号-密码等信息String driverClassName = "dm.jdbc.driver.DmDriver";String url = "jdbc:dm://localhost:5236/";String username = "yxd179";String password = "yxd179";// 加载驱动Class.forName(driverClassName);// 获取数据库连接对象Connection con = (Connection) DriverManager.getConnection(url,username,password);// 获取数据库操作对象PreparesStatement ps = con.preparesStatement("SELECT COUNT(*) FROM TEST;");// 执行sqlResultSet rs= ps.executeQuery();// 这里还可以获取到数据库产品名称DatabaseMetaData metaData = (DatabaseMetaData) con.getMetaData();// 这里为后续提到的在xml指定达梦的databaseId奠定基础System.out.println("数据库产品名称:" + metaData.getDatabaseProductName());后需关闭连接close,释放资源->rs-ps-con
  tk.mybatis:mybatis定制的大业务增强库。
  pagehelper:分页控件,mybatis定制的第二大业务增强库。



Q

DM8达梦数据库在多数据库切换中,其databaseId如何能够被ORM映射框架MyBatis指定访问


A

当 mybatis 在进行 mapper 注入时,当前 sql 方法的 databaseId 和当前数据源的 databaseId 一样?当前 sql 方法的 databaseId 和当前数据源的 databaseId 不一样?当前 sql 未配置 databaseId?

其实,这都是需要我们care到的。当mybatis装配时,若是同一个方法被找到多条sql时,首先,会优先使用 databaseId 相同的 sql。若是没有 databaseId 相同的sql,其次,再使用未配置 databaseId 的 sql,而databaseId 未对应的 sql 不会使用。

i、当获取到的数据源信息为mysql,则执行图一中批量插入insertBatch方法;
ii、当获取到的数据源信息为db2,则执行图二中批量插入insertBatch方法;
iii、当获取到的数据源信息为oracle,则执行图三批量插入insertBatch方法。

上例,这样我们就能极其简易的指定 databaseId,很多小伙伴肯定会说为什么需要这样去指定?其背后的原理又是怎样的,我们是否能够扩展并自定义 databaseId?框架这层的应用真能够提供的这么 perfect 吗?
在上一个Q-A中,我们已经get到了数据库产品的名称,可以从数据源连接对象中去获取,不妨从这里出发。这里先提出一点 little 猜想,mybatis既然能够支持mysql,oracle,db2等等数据库,那么其他关系型数据库?肯定是提供一些这样的入口可以去扩展的,只是各种框架的适配程度不一样,都在不断兼容。网上关于这块的资料并不全面,基于数据库产品名称这条线索,于是,小编封装了独立的适配器sdk,可作达梦等国产数据库适配。当然不同类型的数据库,后续在sdk中去扩展兼容都是可以做到的。
说明:有兴趣的童鞋请点击译点主页,可公众号后台留言,welcome together^_^
记录:在适配的过程中还试出了达梦的幻读,达梦和Oracle一样默认的隔离级别都是读提交;还有特别需要注意的就是,在数据库中varchar类型的字段,比如在mysql中定义varchar(50),在写满的情况下为50个字符,到了达梦这边多也就是varchar(150),当然可自行指定位宽,小编用的字符集是UTF-8,每个字符占三个字节,以三倍,就肯定是装得下的。目前达梦适配现有的框架为:
HiKari+MyBatis3.4.6+tk.MyBatis+PageHelper
  ok,now,接入sdk适配后,启动的效果,大致如下:

  这里,主要对sql分页查询作下阐述:简单的分页可通过手写sql limit...,首先统计count总数,然后计算分页等相关信息,这里不着重说明,当然sql分页控件也是可以去扩展的。PageHelper分页,其主要原理:首先将传递的参数设置到page这个对象中去,然后会接着将page的副本存放入ThreadLoacl中,保证分页时参数互不影响,通过mybatis提供的拦截器,获取ThreadLocal中的值,重新拼装分页sql语句
  接入之后,我们在控制台输出dm sql日志,看一下打印的sql-伪列分页:

  在DM8中正常执行sql,获取结果集:

  而在MySql中,简单limit分页输出是这样的:

  <附注>Debug分页控件源码,其实也很容易理解,分页终是这样去拼装的:
protected BoundSql getPageBoundSql(Object parameterObject) {    String tempSql = sql;    String orderBy = PageHelper.getOrderBy();    if (orderBy != null) {        tempSql = OrderByParser.converToOrderBySql(sql, orderBy);    }    tempSql = localParser.get().getPageSql(tempSql);    return new BoundSql(configuration, tempSql, localParser.get().getPageParameterMapping(configuration, original.getBoundSql(parameterObject)), parameterObject);}
//当然,这里有很多其他的Parser,这里可扩展,可兼容,去适用于不同的数据库public String getPageSql(String sql) {    StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);    sqlBuilder.append("select * from ( select tmp_page.*, rownum row_id from ( ");    sqlBuilder.append(sql);    sqlBuilder.append(" ) tmp_page where rownum <= ? ) where row_id > ?");    return sqlBuilder.toString();}


Q

DM8、MySql、Oracle等数据库切换过程对比,除了各自方言异同,是否还有其他新发现?


A

结论是肯定的,这里仅根据上述控制台输出的sql日志来看,
PageHelper.startPage(0,100)VSPageHelper.startPage(1,100)
引出这样一个小问题-思考:这俩者在DM8、MySql或者其他关系型数据库中会出现不一样的效果吗?若是,又如何去fix^_^
附:由于时间等原因,阐述不一定俱全,本文暂时就到这里。对于国产化这块,搜索可用资源较少希望对各位读者,在适配产化数据库过程中能够有所帮助,欢迎提出宝贵建议^_^

来自:https://mp.weixin.qq.com/s/QIp2mVJLRYHT48LMaso9wA

相关文章