SOD框架使用金仓数据库“踩坑记”,严格来说是使用金仓数据库过程的踩坑记,并不是使用SOD框架来访问金仓数据库才会发生的问题,SOD框架的网友多年前就封装了人大金仓(现在已经改名为“电科金仓”)和达梦数据库的SOD框架数据提供程序,对应的Nuget包名字分别是 PDF.NET.SOD.Dameng.Provider, PDF.NET.SOD.Kingbase.Provider ,所以当我第一次使用金仓数据库遇到问题时候疑惑为什么使用SOD框架会有问题,而别的ORM框架似乎没有这样的问题?

第一个问题:神秘的sys_stat_scan_tables 角色

SOD框架决定全面支持.NET6的时候,对金仓数据访问提供程序也进行了升级,使用的是Kdbndp 的Nuget包 8.0版本,正好它的说明也是只支持.NET6以上版本。升级后的SOD框架金仓数据访问提供程序Nuget包ID为PWMIS.SOD.Kingbase.Provider ,使用它访问最金仓V9数据库的时候出现下面的问题:

"22P02: invalid input value for enum information_schema.table_type_enum: \"FOREIGN\"\r\n\r\nPOSITION: 135"

经排查这个问题发生在调用ADO.NET的DbConnection的抽象方法GetSchema 方法有关,可以使用下面的代码进行测试:

using Kdbndp;
using System.Data; namespace KingbaseTest
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Kingbase For .NET6 Access Test.");
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings[1].ConnectionString;
Console.WriteLine(connStr);
using (KdbndpConnection conn = new KdbndpConnection(connStr))
{
conn.Open();
Console.WriteLine("Database Connected OK.");
DataTable dt = conn.GetSchema();
Console.WriteLine("GetScheme OK,Database has Schemas count:{0}",dt.Rows.Count);
DataTable dt2 = conn.GetSchema("Tables");
Console.WriteLine("Database has tables count:{0}", dt2.Rows.Count);
conn.Close();
}
Console.WriteLine("Test OK");
}
}
}

与金仓技术人员进行多次沟通后,终于发现问题在金仓的MySQL数据库兼容模式下,非System用户访问金仓数据库的时候对于 information_schema.tables 对象没有访问权限:

select table_schema FROM information_schema.tables  group by table_schema;

提示用户没有访问 sys_freespace 函数的权限:
42501: permission denied for function sys_freespace

出现这个问题的时候必须授权当前访问金仓数据库的用户有  sys_stat_scan_tables 角色。

划重点:这个问题很很重要,当你需要使用SOD框架的Code First编码方式使用金仓数据库的时候必须要确保用户有 sys_stat_scan_tables 角色权限。

划重点:这个问题仅出现在MySQL兼容模式中。

第二个问题:令人迷惑的金仓驱动程序版本

当程序运行在金仓为客户定制的某V8版本的MySQL兼容模式数据库上的时候,运行上面这个测试程序又出现了下面的问题:

System.ArgumentException
HResult=0x80070057
Message=A KingbaseES type with the name int16 was not found in the database
Source=Kdbndp
StackTrace:
在 Kdbndp.Internal.KdbndpDatabaseInfo.GetKingbaseTypeByName(String pgName)
在 Kdbndp.TypeMapping.BuiltInTypeHandlerResolver..ctor(KdbndpConnector connector)
在 Kdbndp.TypeMapping.BuiltInTypeHandlerResolverFactory.Create(KdbndpConnector connector)
在 Kdbndp.TypeMapping.ConnectorTypeMapper.Reset()
在 Kdbndp.Internal.KdbndpConnector.<LoadDatabaseInfo>d__199.MoveNext()
在 Kdbndp.Internal.KdbndpConnector.<Open>d__198.MoveNext()
在 Kdbndp.ConnectorPool.<OpenNewConnector>d__34.MoveNext()
在 Kdbndp.ConnectorPool.<<Get>g__RentAsync|31_0>d.MoveNext()
在 Kdbndp.KdbndpConnection.<<Open>g__OpenAsync|47_0>d.MoveNext()
在 Kdbndp.KdbndpConnection.Open()

又是一个复杂的排查过程,在反复确认了数据库的版本之后,金仓技术人员找到了问题原因:驱动程序版本不正确。使用金仓公司指定的,对应的Nuget包名字为 Kdbndp_V9 这个.NET驱动程序后问题果然解决。于是,我不得不用这个驱动程序重新封装了一个SOD框架访问金仓数据库的数据访问提供程序,这就是为什么有PWMIS.SOD.Kingbase.Provider 了之后,还需要 PWMIS.SOD.Kingbase.Provider.Net6V9,PWMIS.SOD.Kingbase.Provider.Net8V9 两个Nuget包的原因。简单总结一下:

  1. PDF.NET.SOD.Kingbase.Provider --SOD框架适用于.NET 4.x 版本的金仓数据访问提供程序;
  2. PWMIS.SOD.Kingbase.Provider --SOD框架适用于.NET 6以上,采用Kdbndp 8.0版本驱动程序,可以访问金仓V8以及V9版本的数据库;
  3. PWMIS.SOD.Kingbase.Provider.Net6V9 --SOD框架使用.NET 6版本的,采用Kdbndp_V9 版本驱动程序,专为金仓V9版本和某些定制的V8版本数据库访问而定制的驱动程序,当然也可以访问V8版数据库。
  4. PWMIS.SOD.Kingbase.Provider.Net8V9 --SOD框架使用.NET 8版本的,采用Kdbndp_V9 版本驱动程序,专为金仓V9版本和某些定制的V8版本数据库访问而定制的驱动程序,当然也可以访问V8版数据库。

综上,你在具体使用金仓数据库的时候需要使用哪个版本的SOD框架提供程序,可以根据情况测试后使用。

划重点:如果你默认使用金仓的Oracle兼容模式,就不会遇到上面两个问题,直接使用PWMIS.SOD.Kingbase.Provider 驱动程序即可。

为了方便在SOD框架中使用上述不同的驱动程序,可以添加一个应用程序配置文件 app.config文件,在connectionStrings 配置节添加如下配置内容:

 <connectionStrings>
<!--
<add name="local1"
connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321"
providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient"/>
<add name="local2"
connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321"
providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient.Net6V9"/>
-->
<add name="default"
connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321"
providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient.Net8V9"/>
</connectionStrings>

然后如下使用金仓数据访问对象:

PWMIS.DataProvider.Data.Kingbase kingbase= AdoHelper.CreateHelper("default");
var dataset= kingbase.ExecuteDataSet("select * from [Table1] ");

第三个问题:神秘的SQL_MODE

像金仓、达梦这样的国产化数据库其诞生之初就号称兼容Oracle,SQL Server,MySQL等市场常用的数据库,声称可以无缝兼容基于这些数据库开发的应用,自然在使用金仓数据库的时候会有数据库兼容性的问题,而我们在通常使用中似乎真的没有遇到兼容性问题,直到遇见了SQL-MODE问题。

问题首先发生在应用程序运行在客户定制的某金仓V8版本出现下面类似的错误:

 syntax error at or near ""Table1""
CREATE SEQUENCE table1_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "Table1"(
"ID" integer DEFAULT nextval('table1_id_seq':regclass) NOT NULL PRIMARY KEY
)

很明显,当前数据库不识别双引号,Oracle数据库对象使用双引号进行标识,MySQL数据库使用反单引号进行标识,由于当前数据库设置成MySQL兼容模式,上面创建表的语句自然报错,正确的应该是:

CREATE SEQUENCE table1_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE `Table1`(
`ID` integer DEFAULT nextval('table1_id_seq':regclass) NOT NULL PRIMARY KEY
)

为啥之前在测试的V9数据库和V8数据库都没有遇到这个问题,在客户定制的V8版本就出现这个问题了呢?请金仓的技术人员排查后发现,原来两个数据库的SQL_MODE设置不一样。

SQL_MODE设置在数据库运行和SQL语句执行中具有重要作用,它可以规范SQL语句的执行行为,控制SQL语法的兼容性, 优化查询性能和安全性,控制数据完整性约束,影响数据类型和格式处理。SQL_MODE的默认值为:

SHOW SQL_MODE;

ONLY_FULL_GROUP_BY,ANSI_QUOTES

在默认情况下,金仓数据库会严格限制GROUP BY子句的使用,要求在SELECT列表中出现的非聚合列必须同时出现在GROUP BY子句中。金仓数据库的SQL_MODE默认值为ONLY_FULL_GROUP_BY。这意味着在默认情况下,金仓数据库会严格限制GROUP BY子句的使用,要求在SELECT列表中出现的非聚合列必须同时出现在GROUP BY子句中SQL_MODE设置为ANSI_QUOTES的作用是改变SQL语句中双引号(")的语义,使其符合ANSI SQL标准。

这也是为何在默认情况下程序运行没有报错的原因,然而客户的数据库中SQL_MODE设置为空,并且数据库兼容模式为MySQL,所以在SQL查询中对于表名字、字段名字必须使用反单引号。

于是,SOD的金仓数据库访问提供程序也开启了设置兼容模式:

private string _DataBaseMode = "Oracle";
private char _dbSplitChar = '"';
/// <summary>
/// 获取或者设置数据库兼容模式,可以指定的模式有Oracle,MySQL,SQLServer,PostgreSQL,默认为Oracle
/// </summary>
public string DataBaseMode
{
get { return _DataBaseMode; }
set
{
string mode = value.ToLower();
if (mode == "oracle" || mode == "postgresql")
{
_DataBaseMode = value;
_dbSplitChar = '"';
}
else if (mode == "mysql")
{
_DataBaseMode = value;
_dbSplitChar = '`';
}
else if (mode == "sqlserver")
{
_DataBaseMode = value;
_dbSplitChar = '[';//']'
}
else
{
throw new Exception("Kingbase Database_mode must one is Oracle,MySQL,SQLServer.");
}
}
}

之后,在程序中这样使用:

var kingbase= new  PWMIS.DataProvider.Data.Kingbase();
kingbase.ConntctionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321";
kingbase.DataBaseMode="MySQL";
var dataset= kingbase.ExecuteDataSet("select * from `Table1` ");

注意:

上面的示例方式是在SOD的金仓数据访问提供程序 PWMIS.SOD.Kingbase.Provider.Net6V9 和 PWMIS.SOD.Kingbase.Provider.Net8V9 V6.0.1版本支持,如果使用 V6.0.0版本,可以全局设置,上面的示例代码修改如下:

var kingbase= new  PWMIS.DataProvider.Data.Kingbase();
kingbase.ConntctionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321";
//V6.0.0 版本使用静态全局设置数据库兼容模式:
PWMIS.DataProvider.Data.Kingbase.DataBaseMode="MySQL";
var dataset= kingbase.ExecuteDataSet("select * from `Table1` ");

第四个问题:数据类型兼容的问题

金仓V8以及V9各个版本的数据类型并不一定完全兼容,并且它与MySQL的类型也不完全兼容,因此建议在数据库迁移的时候,一定要使用Code First方式,由程序字段创建表结构,否则迁移过程将有无尽的痛苦,这里不做过多表述。

由于时间关系,先到这里。

SOD框架使用金仓数据库“踩坑记”的更多相关文章

  1. Spark踩坑记——数据库(Hbase+Mysql)

    [TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...

  2. [转]Spark 踩坑记:数据库(Hbase+Mysql)

    https://cloud.tencent.com/developer/article/1004820 Spark 踩坑记:数据库(Hbase+Mysql) 前言 在使用Spark Streaming ...

  3. Spark踩坑记——数据库(Hbase+Mysql)转

    转自:http://www.cnblogs.com/xlturing/p/spark.html 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库 ...

  4. 通过ODBC接口访问人大金仓数据库

      国产化软件和国产化芯片的窘境一样,一方面市场已经存在性能优越的同类软件,成本很低,但小众的国产化软件不仅需要高价买入版权,并且软件开发维护成本高:另一方面,国产软件目前普遍难用,性能不稳定,Bug ...

  5. QT 之 ODBC连接人大金仓数据库

    QT 之 使用 ODBC 驱动连接人大金仓数据库 获取数据库驱动和依赖动态库 此操作可在人大金仓官网下载与系统匹配的接口动态库,或者从架构数据库的源码中获取驱动和依赖动态库 分别为: 驱动动态库:kd ...

  6. 通过jmeter连接人大金仓数据库

    某项目用的人大金仓数据库,做性能测试,需要用jmeter来连接数据库处理一批数据.jmeter连接人大金仓,做个记录. 1. 概要 在"配置元件"中添加"JDBC Con ...

  7. Spark踩坑记——从RDD看集群调度

    [TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...

  8. Spring @Transactional踩坑记

    @Transactional踩坑记 总述 ​ Spring在1.2引入@Transactional注解, 该注解的引入使得我们可以简单地通过在方法或者类上添加@Transactional注解,实现事务 ...

  9. Vue + TypeScript + Element 搭建简洁时尚的博客网站及踩坑记

    前言 本文讲解如何在 Vue 项目中使用 TypeScript 来搭建并开发项目,并在此过程中踩过的坑 . TypeScript 具有类型系统,且是 JavaScript 的超集,TypeScript ...

  10. <<Python编程:从入门到实践>>踩坑记 Django

    <<Python编程:从入门到实践>>踩坑记 Django Django Python 19.1.1.5 模板new_topic 做完书上的步骤后,对主题添加页面经行测试,但是 ...

随机推荐

  1. 【C#】SuperSocket配置启动UDP服务器

    SuperSocket配置UDP服务器 零.需求 两个设备局域网联机,需要用广播自动搜寻,而SuperSocket1.6的默认AppServer使用的是TCP,但只有UDP才支持广播. 一.解决 推荐 ...

  2. vue & font-awesome

    vue & font-awesome // 使用npm安装依赖 npm install font-awesome@4.7.0 --save --verbose // 会在包管理文件(packa ...

  3. REST API从木愣到够呆

    目前准备写一个API服务,遵循REST规范,因为自己也是第一次接触这玩意,所以就以自我的认知和理解过程来记录,留爪. ##在REST里什么叫资源? 拿数据表为例: 现在有三张表:(此表非彼婊) sch ...

  4. SLAM导航全栈书的正确打开姿势

    SLAM导航全栈书的正确打开姿势 随着人工智能.机器人.无人驾驶等技术的蓬勃发展,作为底层技术基石的SLAM也逐渐被大家所熟知.人工智能技术如果仅仅停留在虚拟的网络和数据之中的话,那么它挖掘并利用知识 ...

  5. JDBC-增删查改操作

    使用场景:测试家族族长分成时需要批量添加家族流水记录,但手动添加和SQL语句添加较为麻烦 操作步骤 运行环境:Java8+IDEA 1.打开IDEA 点击File->New->Projec ...

  6. 🎀spring @conditional介绍及使用

    简介 @Conditional 是 Spring 框架中用于条件化注册 Bean的核心注解,它允许开发者根据特定条件决定是否将某个Bean注册到 Spring 容器中.这一机制在实现多环境配置.模块化 ...

  7. MySQL 中如果发生死锁应该如何解决?

    MySQL 中如果发生死锁应该如何解决? 死锁是指多个事务在执行过程中因资源争用形成的循环等待,导致无法继续执行.MySQL 会自动检测死锁并选择一个事务进行回滚,但我们可以通过优化设计和操作来避免和 ...

  8. AWVS(Acunetix)

    网络安全渗透测试-AWVS(Acunetix)漏洞扫描工具安装与使用教程 AWVS安装与激活 AWVS简介 AWVS(Acunetix Vulnerability Scanner)是一款网络漏洞扫描工 ...

  9. 使用IDEA管理服务器Docker及远程仓库

    目录 配置连接Docker服务器及远程仓库 连接服务器Docker 远程仓库(可选) IDEA管理 确保docker服务器已经开启了远程守护进程访问.[1] 配置连接Docker服务器及远程仓库 连接 ...

  10. 【经验】IDA|python 脚本怎么使用反汇编的变量,以及获取反汇编地址上的值,附 IDA的output窗口被不小心关掉了的打开方式

    文章目录 IDA脚本怎么用变量--怎么获取目标文件内的值(python) 1 获取地址 2 获取地址上的值 可能出现的问题:NameError: name 'Byte' is not defined ...