背景
因为国际局势的变化,国产化替代是如今重要的指导性思想,根据市场环境及公司的要求,需要对产品做达梦数据库的兼容。
1、环境与工具
首先需要部署一个达梦数据库的环境,可以到达梦官网去下载一个安装,我这边有专门的部门同事已经安装好测试环境,不必自己去安装。
使用达梦数据库时,可以用达梦自带的DM数据库管理工具,如果需要使用另外的数据库管理工具,可以下载安装SQLark白灵连接,目前可以免费使用。
c
1、添加达梦数据库驱动依赖
- <!--达梦数据库驱动-->
- <dependency>
- <groupId>com.dameng</groupId>
- <artifactId>DmJdbcDriver18</artifactId>
- <version>8.1.1.193</version>
- </dependency>
复制代码
2、修改配置文件
修改驱动依赖包为:spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver
修改数据库连接url为:jdbc:dm://192.168.122.34:5236?schema=abc
如果不指定schema(即指定使用哪个数据库,在达梦指的是哪个schema),程序启动则会发生找不到对应的表的异常
3、添加方言包
如果程序使用了Hibernate JPA等对象映射框架,还需要引入方言,这里我们可以新建一个类,并进行配置
- package com.car.config;
- import org.hibernate.dialect.Dialect;
- import org.hibernate.dialect.function.VarArgsSQLFunction;
- import org.hibernate.type.StringType;
- import java.sql.Types;
- /**
- * @Author cwl
- * @Date 2025-02-08 16:27
- */
- public class DmDialect extends Dialect {
- public DmDialect() {
- super();
- registerColumnType(Types.BIT, "boolean");
- registerColumnType(Types.BIGINT, "bigint");
- registerColumnType(Types.SMALLINT, "smallint");
- registerColumnType(Types.TINYINT, "tinyint");
- registerColumnType(Types.INTEGER, "integer");
- registerColumnType(Types.CHAR, "char(1)");
- registerColumnType(Types.VARCHAR, "varchar($l)");
- registerColumnType(Types.FLOAT, "float");
- registerColumnType(Types.DOUBLE, "double");
- registerColumnType(Types.DATE, "date");
- registerColumnType(Types.TIME, "time");
- registerColumnType(Types.TIMESTAMP, "timestamp");
- registerColumnType(Types.BINARY, "binary");
- registerColumnType(Types.VARBINARY, "varbinary($l)");
- registerColumnType(Types.BLOB, "blob");
- registerColumnType(Types.CLOB, "clob");
- registerColumnType(Types.BOOLEAN, "boolean");
- registerFunction("concat", new VarArgsSQLFunction(StringType.INSTANCE, "", "||", ""));
- }
- }
复制代码- spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver
- spring.datasource.url=jdbc:dm://192.168.122.34:5236?schema=abc
- spring.datasource.username=SYSDBA
- spring.datasource.password=root
- #达梦方言包
- spring.jpa.database-platform=com.lancoo.car.config.DmDialect
复制代码
3、数据库脚本适配
原MySQL数据库中创建数据库的脚本,可以做一些修改以适配达梦数据库。
1、创建数据库语句调整
MySQL创建数据库语句
- CREATE DATABASE `name` ;
- USE `campuscar`;
复制代码
替换为以模式(schema)形式的创建数据库
- CREATE SCHEMA "name" ;
- ALTER SESSION SET CURRENT_SCHEMA = "name";/*设置当前会话*/
复制代码
2、去掉utf-8等字符的设置
3、表名的单引号改为双引号,如`tableName`改为"tableName"
4、去掉 ON UPDATE CURRENT_TIMESTAMP, 改为触发器实现
5、索引单独实现
6、tinyint\int\bigint 后面不带数字
7、设置自增主键
- # 从 1 开始,每次增加 1
- "id" int IDENTITY(1, 1) NOT NULL COMMENT '主键id',
复制代码
4、sql语句适配
1、 ifnull 名称改为 COALESCE
例如;ifnull(s.roomId,ao.roomId) as roomId 改为 COALESCE(s.roomId,ao.roomId) as roomId
2、 group_concat 方法需要修改
例如:group_concat(assetName) as applyAssetName 改为 LISTAGG(assetName, ', ') WITHIN GROUP (ORDER BY assetName) AS applyAssetName
5、可能会遇到的异常
1、达梦报错,发生变量空间溢出异常
原因是一次性插入了8838条记录,超过了在达梦数据库配置的缓存或者内存上限,可以对达梦数据库重新配置优化
也可以在程序中将超过1000个元素的列表拆开为小批量插入,设置1000为最大长度,拆开进行数据插入
这里可以新建一个工具类,使用Stream Api进行实现对数组的分割
- package com.car.util;
- import java.util.List;
- import java.util.stream.Collectors;
- import java.util.stream.IntStream;
- /**
- * @Author cwl
- * @Date 2025-02-10 16:08
- */
- public class CommonUtil {
- /**
- * 兼容达梦,列表数据插入之前先按默认最大1000分割列表
- * @param largeList
- * @param <T>
- * @return
- */
- public static <T> List<List<T>> DmInsertListSplit(List<T> largeList){
- return split(largeList, 1000);
- }
- /**
- *
- * @param largeList 原数组
- * @param chunkSize 目标数组长度
- * @return
- */
- public static <T> List<List<T>> split(List<T> largeList, int chunkSize){
- List<List<T>> smallLists = IntStream.range(0, largeList.size() / chunkSize + 1)
- .mapToObj(i -> largeList.subList(i * chunkSize, Math.min((i + 1) * chunkSize, largeList.size())))
- .collect(Collectors.toList());
- return smallLists;
- }
- }
复制代码
大量数据的列表分割成小列表后,成功将数据插入达梦数据库
2、设置了自增主键后,不能手动给主键赋值,如果需要手动赋值,需要先用命令打开限制
用命令打开限制
- # 若需手动赋值,需要先在当前环境打开
- SET IDENTITY_INSERT MyTable ON; -- 允许对自增列赋值
- # 数据插入后,建议关闭
- SET IDENTITY_INSERT MyTable OFF; -- 关闭对自增列的显式赋值
复制代码
3、给字段赋值空字符串时需要用单引号
4、sql脚本内有注释会导致 Cause: dm.jdbc.driver.DMException: 序列号无效,需要将注释删除
5、使用SQLark工具进行sql操作时,注意更新的sql操作需要加上事务提交 commit,否则更新不会提交,并且该事务一直未提交,会影响到其他事务的正常进行,达梦会报一些 jdbcType=null, 序列号无效之类的错误
- update parent set parName = 'chen' where stuUserId = 'S01001';
- # 如果没有隐式的事务自动提交,则此处需要显式地进行事务提交
- commit ;
复制代码
5、其他
(1)达梦不兼容 ON DUPLICATE KEY ,无法用此实现插入或更新的操作,例如
- @Insert({"<script>",
- "insert into "admin" (userId,userName)",
- " values",
- " (#{userId},#{userName})",
- " ON DUPLICATE KEY",
- " update userName = #{userName}",
- "</script>"})
复制代码
(2)admin是达梦的关键字,若有数据库表命名为admin时,需要加上 " ",如 “admin”
- @Update("update "admin" set userName = #{userName} where userId = #{userId}")
复制代码
(3)获取自增主键,statement = “SELECT IDENT_CURRENT(‘campuscar.gate’)”
在mysql中获取自增主键
- @Insert("insert into gate" +
- " (gateName,gateAddr,gateType)" +
- " values(#{param.gateName},#{param.gateAddr},#{param.gateType})")
- @SelectKey(statement = "select last_insert_id()", keyProperty = "param.id", before = false, resultType = int.class)
- int addGate(@Param("param") GateParam param);
复制代码
在达梦中获取自增主键
- @Insert("insert into gate" +
- " (gateName,gateAddr,gateType)" +
- " values(#{param.gateName},#{param.gateAddr},#{param.gateType})")
- @SelectKey(statement = "SELECT IDENT_CURRENT('campuscar.gate')",
- keyProperty = "param.id",
- before = false,
- resultType = int.class)
- int addGate(@Param("param") GateParam param);
复制代码