数据库访问抽象层系列-1(介绍数据库编程接口及数据库访问抽象层概念)

来源:互联网 发布:mac装好的软件在哪里 编辑:程序博客网 时间:2024/06/03 21:12

摘要
本人最近完成了一个封装数据库访问抽象层的项目。我们开发的数据库访问抽象层作为分布式集群基础平台的一个组件。可以支持不同数据库编程接口(OCI、mysql、ODBC、pgsql)等。本系列博客主要分享一下我在开发数据库访问抽象层组件的遇到的问题和收获。
在本文主要介绍数据库访问抽象层的概念,常见的几种数据库编程接口ODBC、JDBC、OLE-DB、ADO、OCI等。

数据库访问抽象层的意义
数据库访问抽象层是封装了数据库底层操作的介于应用逻辑程序和数据之间的中间件。目的是为了隐藏因为数据库方言的不同,而在存取数据中的差异。使整个系统在变更商业数据库的时候,做到改动最小,或者只需要修改数据库的配置文件即可。
提供一种轻型、清晰、方便的 API ,统一各种不同 RDBMS 库的共有特性,但不排除更高级的特性。通过动态库提供可选的较大程度的抽象/兼容性。针对以上理解,本文共分为四个部分:
 第一部分是1、2章,主要探讨几种数据库编程接口的选择;
 第二部分是第3章,详细描述ODBC的安装与配置;
 第三部分是4、5章,讨论ODBC的数据类型以及支持的函数;
 第四部分是6章,编写一个ODBC编程接口的测试程序
 第五部分是第7章,作为附录介绍ODBC编程常遇到的两个问题。


1. 概念
服务器后台数据库的编程接口包括OCI、JDBC、ODBC、OLE-DB、ADO,本小节分别阐述几种概念并分析他们之间的关系。
1.1. 什么是JDBC?
Java语言访问数据库的一种规范,是一套API。
JDBC(Java Database connectivity)API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库。比如建立数据库连接、执行SQL语句进行数据的存取操作。
JDBC规范采用接口和实现分离的思想设计了Java数据库的编程框架。接口包含在Java.sql及Javax.sql包中,其中Java.sql属于JavaSE,Javax.sql属于JavaEE。这些接口的实现类叫做数据库驱动程序,由数据库的厂商或其它的厂商或个人提供。

这里写图片描述
图1-1

1.2. 什么是ODBC?
ODBC(Open Database Connectivity,开放数据库互连)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。
应用程序要访问一个数据库,首先必须用ODBC管理器注册一个数据源,管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与具体数据库的联系。这样,只要应用程序将数据源名提供给ODBC,ODBC就能建立起与相应数据库的连接。
达梦数据库(DM DATABASE,以下简称DM)的DM ODBC 3.0遵照Microsoft ODBC 3.0规范设计与开发,实现了ODBC应用程序与DM的互连接口。用户可以直接调用DM ODBC 3.0接口函数访问DM,也可以使用可视化编程工具如Visual C++、C++ Builder、PowerBuilder等利用DM ODBC 3.0访问DM。

图1-2
图 1-2

1.3. 什么是OLE-DB
OLE DB(Object Link and embed 即对象连接与嵌入。)是微软的战略性的通向不同的数据源的低级应用程序接口。OLE DB不仅包括微软资助的标准数据接口开放数据库连通性(ODBC)的结构化问题语言(SQL)能力,还具有面向其他非SQL数据类型的通路。 作为微软的组件对象模型(COM)的一种设计,OLE DB是一组读写数据的方法(在过去可能被称为渠道)。OLD DB中的对象主要包括数据源对象、阶段对象、命令对象和行组对象。使用OLE DB的应用程序会用到如下的请求序列:初始化OLE 连接到数据源 发出命令 处理结果 释放数据源对象并停止初始化OLE
OLE DB标准中定义的新概念—-OLE DB将传统的数据库系统划分为多个逻辑组件,这些组件之间相对独立又相互通信。这种组件模型中的各个部分被冠以不同的名称:数据提供者(Data Provider)。 提供数据存储的软件组件,小到普通的文本文件、大到主机上的复杂数据库,或者电子邮件存储,都是数据提供者的例子。有的文档把这些软件组件的开发商也称为数据提供者。

1.4. 什么是ADO
微软公司的ADO (ActiveX Data Objects)是一个用于存取数据源的COM组件。它提供了编程语言和统一数据访问方式OLE DB的一个中间层。允许开发人员编写访问数据的代码而不用关心数据库是如何实现的,而只用关心到数据库的连接。访问数据库的时候,关于SQL的知识不是必要的,但是特定数据库支持的SQL命令仍可以通过ADO中的命令对象来执行。
ADO被设计来继承微软早期的数据访问对象层,包括RDO (Remote DataObjects)和DAO(Data Access Objects)。ADO在1996年冬被发布。
ADO包括了6个类:Connection,Command,Recordset,Errors,Parameters,Fields。

1.5. OLE-DB 和ODBC的区别
由于OLEDB和ODBC 标准都是为了提供统一的访问数据接口,所以曾经有人疑惑:OLE DB 是不是替代ODBC 的新标准?答案是否定的。实际上,ODBC 标准的对象是基于SQL 的数据源(SQL-Based Data Source),而OLE DB 的对象则是范围更为广泛的任何数据存储。从这个意义上说,符合ODBC 标准的数据源是符合OLE DB 标准的数据存储的子集。

1.6. ADO、OLE-DB和ODBC的关系
通俗点 OLE DB和ODBC都是最底层的东西,而ADO对象给我们提供了一个“可视化”,和应用层直接交互的组件,我们不用过多的关注OLE-DB的内部机制,只需要了解ADO通过OLE-DB创建数据源的几种方法即可,就可以通过ADO轻松地获取数据源。ADO是应用程序和数据底层的一个中间层,ADO对象通过OLE-DB间接取得数据库中的数据。OLE-DB只是提供了通向各种数据库的一个通用接口,可以用下图2-1来表示:

图1-3
图1-3
1.7 OCI编程接口的简介

请参照如下连接的简介:
http://baike.baidu.com/link?url=i57cAhZrzMiGI1lNLp3se9Tkg_9BSdwDp3K_vVS-IwR5m90mcdsjv_366kASh46xjPRM8kYJs3K0djJG9BSOZ_
pgsql编程接口的简介:
http://baike.baidu.com/link?url=k0sIoIwe9U1ySbQLk6CR3soEFw9smmeF35Q3D7Cz-Etc4GjBgFIc-Ze288J07ECe6xNY6d-dgcgJ3O4A02O1mK


2. 标准接口的选择
2.1. 为什么不选择JDBC?
由1.1和1.2两个小节可知,JDBC属于Java数据库编程接口,ODBC支持C/C++等变成语言。根据需求我们需要封装一套通用C/C++数据库封装层API,故JDBC不可行。

2.2. 为什么不选择ADO?
由2小节可知,ADO是对OLE-DB的封装,优势主要在于易用性和可视化,本质上ADO是数据库访问抽象层。而我们的需求是提供类似PCS系统DBAPI的API接口函数,可以方便的支持不同的数据库。如果选择ADO,则我们需要在ADO的基础上再封装一层API接口,而数据库操作是偏向底层。过多的封装在一定程度上降低整个系统的性能。基于以上原因而不选择ADO。

2.3. 用OLE-DB还是ODBC?
从网络查找资料了解到选择OLE-DB和ODBC的一些基本的原则:
① 非OLE 环境 —— 如果要访问支持ODBC 的数据库,而该数据库又在不支持OLE 的服务器上,那么ODBC 是最好的选择。
② 非SQL 环境 —— ODBC 在处理SQL 时非常出众。处理非SQL 数据库时,OLE-DB则具有非常明显的优势。
③ OLE 环境 —— 对支持OLE 的服务器来说,选择OLE-DB 还是ODBC 也许是希望各半。如果有ODBC 驱动程序可供利用,那么使用ODBC 是一个好主意;否则,就只有选择OLE-DB了。
④ 所需的互操作性 如果需要可互操作的数据库部件,那么只有选择OLE-DB。
综合以上基本原则②和我们的需求,个人认为我们更适合选择ODBC编程接口。
结论
根据以上原则,选择ODBC作为待封装的编程接口。


3. ODBC的安装与配置
使用ODBC 方法访问一个DM 数据库服务器之前,必须先对自己的应用程序所用的ODBC 资源进行配置。本小节将介绍如何在linux环境下安装和配置ODBC 资源。
在 Linux 上配置ODBC 数据源的方式有两种,手动配置和图形配置。下面将以麒麟操作系统为例详细描述Linux环境安装unixODBC数据源、手动配置达梦ODBC数据源。

3.1. 编译和安装unixODBC数据源
① 将压缩包unixODBC-2.3.0.tar.gz上传到 linux /usr/local 下,然后执行 tar -xzvf unixODBC-2.3.0.tar.gz
② 进入cd /usr/local/unixODBC-2.3.0 执行 ./configure –prefix=/usr/local/unixODBC-2.3.0 –includedir=/usr/include –libdir=/lib64/lib -bindir=/usr/bin –sysconfdir=/etc
③ 执行 make
④ 执行 make install
⑤ 通过 odbc_config –version 确定odbc安装是否成功,显示2.3.0表示已经成功安装。

3.2. 配置达梦ODBC资源
达梦数据库ODBC达梦数据库驱动程序如下表格3-1所示。达梦ODBC有两个配置文件odbcinst.ini和odbc.ini。odbcinst.ini文件负责配置DM 数据库ODBC 驱动,odbc.ini文件负责配置 DSN(data source name)。这两个文件的路径通过命令获取,如下图所示。达梦ODBC需要的动态库:libdmapi.so、libdmcrypto_engine.so、libdmodbc.so、libdmucvt.so、libdmzip.so。

[root@localhost etc]# odbc_config --odbcini/etc/odbc.ini[root@localhost etc]# odbc_config --odbcinstini/etc/odbcinst.ini

图3-1

odbcinst.ini文件配置项:
① [dm]设置DSN(data source name)-即设置数据源名。
② Driver指定动态驱动库路经。
odbc.ini文件配置项:
① Driver指定动态驱动库路经;
② SERVER指定数据库节点IP地址
③ UID指定登陆用户名
④ PWD指定登陆密码
⑤ TCP_PORT指定端口号,默认12345
现在通过实例配置文件odbc.ini需要修改字段SERVER,使之等于达梦数据库服务端节点IP。odbcinst.ini需要修改字段Driver,使之与动态库libdmodbc.so实际路径相同(注:与达梦手册中配置不同部分,以本文档为准)。图3-3和图3-4是备调测试系统BDTEST sgs1-net1节点上两个文件配置的两个实例:

[dm]Description = dmDriver =dmSERVER =192.168.200.100UID=SYSDBAPWD=SYSDBATCP_PORT=12345

图3-3 odbc.ini文件

[dm]Description=dmDriver = /etc/dm_odbc_lib/libdmodbc.so

表格 3 4文件odbcinst.ini

配置完成后在任意路径,通过命令测试连接。方法如下图所示:

[root@localhost /]# isql dm SYSDBA SYSDBA+---------------------------------------+| Connected!              ||                        || sql-statement            || help [tablename]          || quit                    ||                        |+---------------------------------------+

图 3-5 测试连接


4. ODBC的数据类型
客户程序可以通过SQLGetTypeInfo 函数来获取DM ODBC 3.0 支持的数据类型信息。由
SQLGetTypeInfo 返回的数据类型是数据源所支持的数据类型,它们是预备用于DDL
(DataDefinitionLanguage)语句的。调用 DM ODBC 3.0 的SQLGetTypeInfo,返回支持的数据类型如图5-1如下:

图5-1
图4-1
注:要支持interval 数据类型,必须在数据源中进行设置,否则将不提供对该类数据类型的支持。变长字符串的最大长度实际上是受库块大小的约束,上面的约束是在库大小为8K 时的最大长度。


5. 达梦ODBC支持的函数
DM ODBC 3.0 遵照Microsoft ODBC 3.0 规范设计与开发

客户程序可以通过SQLGetFunctions 函数来获取DM ODBC 3.0 支持的函数信息。由SQLGetFunctions 返回的函数列表是数据源所支持的函数。以下按照类型分类列出了DM ODBC 3.x 提供的函数。应用程序能够通过调用SQLGetFunctions 来获得指定函数的支持信息。
5.1. 连接到数据源
下面的函数用于连接到数据源:
(1)SQLAllocHandle:分配环境、连接、语句或者描述符句柄。
(2)SQLConnect:建立与驱动程序或者数据源的连接。访问数据源的连接句柄包含了包
括状态、事务申明和错误信息的所有连接信息。
(3)SQLDriverConnect:与SQLConnect 相似,用来连接到驱动程序或者数据源。但它比
SQLConnect 支持数据源更多的连接信息,它提供了一个对话框来提示用户设置所有的连接信息
以及系统信息表没有定义的数据源。
(4)SQLBrowseConnect:支持一种交互方法来检索或者列出连接数据源所需要的属性和
属性值。每次调用函数可以获取一个连接属性字符串,当检索完所有的属性值,就建立起与数
据源的连接,并且返回完整的连接字符串,否则提示缺少的连接属性信息,用户根据此信息重
新输入连接属性值再次调用此函数进行连接。
5.2. 获取驱动程序和数据源信息
下面的函数用来获取驱动程序和数据源信息:
(1)SQLDataSources:能够被调用多次来获取应用程序使用的所有数据源的名字。
(2)SQLDrivers:返回所有安装过的驱动程序清单,包括对它们的描述以及属性关键字。
(3)SQLGetInfo:返回连接的驱动程序和数据源的元信息。
(4)SQLGetFunctions:返回指定的驱动程序是否支持某个特定函数的信息。
(5)SQLGetTypeInfo:返回指定的数据源支持的数据类型的信息。
5.3. 设置或者获取驱动程序属性
下面的函数用来设置或者获取驱动程序属性:
(1)SQLSetConnectAttr:设置连接属性值。
(2)SQLGetConnectAttr:返回连接属性值。
(3)SQLSetEnvAttr:设置环境属性值。
(4)SQLGetEnvAttr:返回环境属性值。
(5)SQLSetStmtAttr:设置语句属性值。
(6)SQLGetStmtAttr:返回语句属性值。
5.4. 设置或者获取描述符字段
下面的函数用来设置或者获取描述符字段:
(1)SQLGetDescField:返回单个描述符字段的值。
(2)SQLGetDescRec:返回当前描述符记录的多个字段的值。
(3)SQLSetDescField:设置单个描述符字段的值。
(4)SQLSetDescRec:设置描述符记录的多个字段。
5.5. 准备SQL 语句
下面的函数用来准备SQL 语句:
(1)SQLPrepare:准备要执行的SQL 语句。
(2)SQLBindParameter:在SQL 语句中分配参数的缓冲区。
(3)SQLGetCursorName:返回与语句句柄相关的游标名称。
(4)SQLSetCursorName:设置与语句句柄相关的游标名称。
(5)SQLSetScrollOptions:设置控制游标行为的选项。
5.6. 提交SQL 请求
下面的函数用来提交SQL 请求:
(1)SQLExecute:执行准备好的SQL 语句。
(2)SQLExecDirect:执行一条SQL 语句。
(3)SQLNativeSql:返回驱动程序对一条SQL 语句的翻译。
(4)SQLDescribeParam:返回对SQL 语句中指定参数的描述。
(5)SQLNumParams:返回SQL 语句中参数的个数。
(6)SQLParamData:与SQLPutData 联合使用在运行时给参数赋值。
(7)SQLPutData:在SQL 语句运行时给部分或者全部参数赋值。
5.7. 检索结果集及其相关信息
下面的函数用来检索结果集及其相关信息:
(1)SQLRowCount:返回INSERT、UPDATE 或者DELETE 等语句影响的行数。
(2)SQLNumResultCols:返回结果集中列的数目。
(3)SQLDescribeCol:返回结果集中列的描述符记录。
(4)SQLColAttribute:返回结果集中列的属性。
(5)SQLBindCol:为结果集中的列分配缓冲区。
(6)SQLFetch:在结果集中检索下一行元组。
(7)SQLFetchScroll:返回指定的结果行。
(8)SQLGetData:返回结果集中当前行某一列的值。
(9)SQLSetPos:在取到的数据集中设置游标的位置。这个记录集中的数据能够刷新、更
新或者删除。
(10)SQLBulkOperations:执行块插入和块书签操作,其中包括根据书签更新、删除或者
取数据。
(11)SQLMoreResults:确定是否能够获得更多的结果集,如果能就执行下一个结果集的
初始化操作。
(12)SQLGetDiagField:返回一个字段值或者一个诊断数据记录。
(13)SQLGetDiagRec:返回多个字段值或者一个诊断数据记录。
5.8. 取得数据源系统表的信息
下面的函数用来取得数据源系统表的信息:
(1)SQLColumnPrivileges:返回一个关于指定表的列的列表以及相关的权限信息。
(2)SQLColumns:返回指定表的列信息的列表。
(3)SQLForeignKeys:返回指定表的外键信息的列表。
(4)SQLPrimaryKeys:返回指定表的主键信息的列表。
(5)SQLProcedureColumns:返回指定存储过程的参数信息的列表。
(6)SQLProcedures:返回指定数据源的存储过程信息的列表。
(7)SQLSpecialColumns:返回唯一确定某一行的列的信息,或者当某一事务修改一行的
时候自动更新各列的信息。
(8)SQLStatistics:返回一个单表的相关统计信息和索引信息。
(9)SQLTablePrivileges:返回相关各表的名称以及相关的权限信息。
(10)SQLTables:返回指定数据源中表信息。
5.9. 终止语句执行
下面的函数用来终止语句执行:
(1)SQLFreeStmt:终止语句执行,关闭所有相关的游标,放弃没有提交的结果,选择释
放与指定语句句柄相关的资源。
(2)SQLCloseCursor:关闭一个打开的游标,放弃没有提交的结果。
(3)SQLCancel:放弃执行一条SQL 语句。
(4)SQLEndTran:提交或者回滚事务。
5.10. 中断连接
下面的函数处理中断连接的任务:
(1)SQLDisconnect:关闭指定连接。
(2)SQLFreeHandle:释放环境、连接、语句或者描述符句柄。
5.11. ODBC编程:
使用ODBC编程的基本步骤如下图所示
5-2
图5-2

6编写两个简单建立连接和断开连接的示例程序

6.1建立连接:

int DMODBCDBApi::Connect(const char *pszConnectStr,const char *pszUser,const char *pszPasswd){// 检测返回代码是否为成功标志,当为成功标志时返回TRUE,否则返回FALSE#define RC_SUCCESSFUL(rc) ((rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO)// 检测返回代码是否为失败标志,当为失败标志时返回TRUE,否则返回FALSE#define RC_NOTSUCCESSFUL(rc) (!(RC_SUCCESSFUL(rc)))    // 申请一个环境句柄    SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);    // 设置环境句柄的ODBC版本    SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,    SQL_IS_INTEGER);    // 申请一个连接句柄    SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);    sret = SQLConnect(hdbc, (SQLCHAR *)pszConnectStr, SQL_NTS, (SQLCHAR *)pszUser, SQL_NTS, (SQLCHAR*)pszPasswd, SQL_NTS);    if (RC_NOTSUCCESSFUL(sret))    {        // 连接数据源失败! 进行相应的错误处理        printf("RC_NOTSUCCESSFUL!!!\n");        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);        SQLFreeHandle(SQL_HANDLE_ENV, henv);        return -1;    }    connected = true;    printf("CONNECT SUCCESSFUL!!!\n");    return 0;}

6.2 断开连接的示例程序

int DMODBCDBApi::DisConnect(){    // 断开与数据源之间的连接    SQLDisconnect(hdbc);    // 释放连接句柄    SQLFreeHandle(SQL_HANDLE_DBC, hdbc);    // 释放环境句柄    SQLFreeHandle(SQL_HANDLE_ENV, henv);    // 连接状态为断开    connected = false;    hdbc = NULL;    henv = NULL;    return 0;}

6.3 Makefile编写的关键点注意

INCDIR = -I. -I/usr/include/LIBDIR = -L/lib64/lib LIBS = -lodbc                           #连接的是UnixODBC数据源动态库,而不是达梦ODBC动态库

7. 附录
在探索过程中发现,觉得在以后编程实战也可能遇到的问题记录如下:
7.1. 调用达梦动态库段错误
解决方法:
编写makefile的时候,连接的动态库是UnixODBC数据源的动态库而非达梦的动态库;
7.2. 打不开动态库
如下图所示:cannot open shared object file: No such file or directory

图7-2

sgs1-net2:/lib64/lib # ls libodbc.so.1
libodbc.so.1
但是ldd命令找不到,说明程序运行加载动态库的时候未搜索到libodbc.so.1。
于是找到解决方案:
vi /etc/ld.so.conf
加入路径/lib64/lib,如下所示:
7-2
图 7-2
执行命令/sbin/ldconfig –v 更新程序加载动态库的路径

0 0
原创粉丝点击