关于统一数据访问的设想 (200分)

  • 主题发起人 主题发起人 ddev
  • 开始时间 开始时间
D

ddev

Unregistered / Unconfirmed
GUEST, unregistred user!
关于统一数据访问的设想
UDA(Universal data-access interface) 为统一数据资源访问接口,由以下几个标准的例程构成:

uda_connect_server();
uda_disconnect_server();
uda_attach_database();
uda_detach_database();
uda_exec_sql();
uda_prepare_query();
uda_exec_query();
uda_close_query();
uda_fetch_row();
uda_fetch_field();
uda_fetch_field_int();
uda_fetch_field_float();
uda_fetch_field_blob();

以上几个函数为标准访问例程。针对不同的数据库系统,
接口的实现可能会有差异,但对外部而言,即是透明的。

UDA 定义了一个统一的 HUDAOBJ 对象(实际上是一个结构),
相当于一个数据库的句柄对象。

struct UDAOBJ {
DWORD dwDBType;
char szUser[MAX_USER];
char szPasswd[MAX_PASSWD];
DWORD dwReserved;
void* lpDesc;
}
typedef UDAOBJ* HUDAOBJ;

该对象中,dwDBType 定义数据库类型,如:
UDA_DBT_FOXPRO,
UDA_DBT_MSSQL,
UDA_DBT_INTERBASE,
UDA_DBT_MYSQL
而 lpDesc 则指向一个数据库的描述结构。不同的数据库的描述
是不同的,如:
Foxpro :
struct UDADESC_FOXPRO {
char db_name[MAX_DBNAME];
char tb_name[MAX_TABLE_NAME];
}
Interbase:
struct UDADESC_IB {
char db_name[MAX_DBNAME];
isc_db_handle db;
isc_tr_handle tr;
isc_stmt_handle stmt;
XSQLDA* xsqlda;
XSQLVAR* xsqlvar;
...
}
MySQL:
struct UDADESC_MYSQL {
MYSQL* db;
MYSQL_RES* db_res;
MYSQL_ROW db_row;
MYSQL_FIELD* db_field;
...
}
这样,用户只要传递一个 HUDAOBJ,接口自动定位数据访问。
如果用户只希望执行 SQL,而不需要返回,如:DELETE,UPDATE,
则执行 uda_exec_sql(),而如果需要返回,则应按下列步骤:
//准备查询
uda_prepare_query();
//运行查询
uda_exec_query();
//检索记录
while (uda_fetch_row() == 0)
{
//检索字段
string s = uda_fetch_field(fd_name);
int n = uda_fetch_field_int(fd_name);
float v = uda_fetch_field_float(fd_name);
//BLOB 另行处理
}
//关闭查询
close_query();
目的:采用这种方式后,应用系统与数据库就可以完全隔开,如果改变
数据库模式,如:INTERBASE->MSSQL,则只要修改一下库类型即可。

------->>> 因为查询接口是通用的,所以连基本的例程都可以保持不动!


>>>>>>>>> 该接口正在编码中,希望大家能够多提建议和意见!
 
呵呵。也不是不行。关键问题是:
1.效率?
2.如何组织一个行或者多个行的数据到数据库服务器?
我倒希望调用方式更方便一些,如:
SimpleQueryForInteger
SimpleQueryForFloat
SimpleQueryForString
 
to barton:
1、效率
由于是直接 API 处理,本身函数效率应该不是问题。
但可能没有缓冲机制,如果应用需要缓冲,则必须在应用
中完成;其二是不能直接连接数据敏感组件,全部需要
手工。简单地说:这是一个 SQL 转向代理。
2、查询
我这个设想并不囿于哪一个数据库。因为每种数据库的
SQL 表达各有差异。因此,函数对此不作任何内置的假设。
如:uda_exec_sql(HUDAOBJ hUDA, const char* sql_expr);
函数只需判断 hUDA 的 dwDBType,例种中(实现函数集)
肯定有一个单独的例程,如:uda_inter_exec_sql_mssql()
函数,是针对 MSSQL 的处理。
虽然 UDAOBJ 是一个大结构,但可以在应用初始化时完成,
并且函数中永远传址,所以不会有影响。
比如:
我现在编程中主要用 IB6.0 数据库。我避开了 VCL 的 IB
组件,直接用 ib 的 API 构建了一个 IBSQL 类。访问速度,
至少在我看来,要比 IB 快!因为直接是 API 调用,不过就
是处理中没那么直接---当然我在 IBSQL 类中解决了一些常用
的或者“总是”要的处理,如:XSQLDA 的分配。
欢迎讨论!
 
这是我以前实现的一个 MySQL 的类,只实现了一些常用的基本功能,
而另一个 IBSQL 的实现大致与些相似。如果大家想看看,就下次贴出吧。
(文件大了,不好意思)
## 文件 mysqlw.h
//---------------------------------------------------------------------------
#ifndef __MySQL_WIN32
#define __MySQL_WIN32
//---------------------------------------------------------------------------
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "include/mysql.h"
//---------------------------------------------------------------------------
typedef enum_field_types MYSQL_FIELD_TYPE;
const static MAX_SQL_SIZE = 4096;
class CMySQL
{
private://MySQL 对象
MYSQL* FMySQL;
MYSQL_RES* FMySQL_Result;
MYSQL_ROW FMySQL_Row;
MYSQL_FIELD* FMySQL_Fields;
int FMySQL_FieldCount;
int FMySQL_RowOffset;
private:
void ResetQueryParams();
public: //构造函数
CMySQL();
~CMySQL();
public: //连接函数
void ConnectMySQL(const char* = NULL, const char* = NULL, const char* = NULL, const unsigned int = MYSQL_PORT);
void ConnectMySQL2(const char*host, const char*, const char*, const char*, unsigned int, const char*, unsigned int);
void DisconnectMySQL();
void ResetMySQL();
public: //数据库管理
bool DatabaseExist(const char* db_name);
void CreateDatabase(const char* db_name);
void SelectDatabase(const char* db_name);
void DeleteDatabase(const char* db_name);
char* GetCurrSelectDatabaseName();
public: //数据表管理
unsigned int GetTableCount(const char* db_name);
void DeleteTable(const char* tb_name);
void OptimizeTable(const char* tb_name);
void RenameTable(const char* old_tbname, const char* new_tbname);
void EmptyTable(const char* tb_name);
public: //查询&amp;结果集处理
int ExecSQL(const char* sql_express);
unsigned int GetAffectedRows();
void OpenQuery();
void CloseQuery();
unsigned int GetRecordCount();
bool IsEof();
bool IsBof();
void Next();
void Prev();
public: //结果集字段(值)访问
unsigned int GetFieldCount();
char* GetFieldName(int);
bool FieldExists(const char* fd_name);
int GetFieldType(const char* fd_name);
char* FetchFieldAsBlob(const char* fd_name);
char* FetchFieldAsString(const char* fd_name);
int FetchFieldAsInteger(const char* fd_name);
bool FetchFieldAsBool(const char* fd_name);
do
uble FetchFieldAsFloat(const char* fd_name);
};
#endif
## 文件 mysqlw.cpp
//---------------------------------------------------------------------------
#include "mysqlw.h"
//---------------------------------------------------------------------------
int my_strtol(const char* s)
{
char* stopstring;
return strtol(s, &amp;stopstring, 10);
}
double my_strtod(const char* s)
{
char* stopstring;
return strtod(s, &amp;stopstring);
}
//---------------------------------------------------------------------------
CMySQL::CMySQL()
{
FMySQL = new MYSQL;

mysql_init(FMySQL);
mysql_real_connect(FMySQL, NULL, NULL, NULL, NULL, MYSQL_PORT, NULL, 0);

}
CMySQL::~CMySQL()
{
mysql_close(FMySQL);
}

//---------------------------------------------------------------------------
void CMySQL::ResetQueryParams()
{
FMySQL_Result = NULL;
FMySQL_FieldCount = 0;
FMySQL_Fields = NULL;
FMySQL_Row = NULL;
}
//---------------------------------------------------------------------------
void CMySQL::ConnectMySQL(const char* host, const char* user, const char* passwd,
const unsigned int port)
{
mysql_close(FMySQL);
mysql_real_connect(FMySQL, host, user, passwd, NULL, port, NULL, 0);
}
void CMySQL::ConnectMySQL2(const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket, unsigned int clientflag)
{
mysql_close(FMySQL);
mysql_real_connect(FMySQL, host, user, passwd, db, port, unix_socket, clientflag);
}
void CMySQL::DisconnectMySQL()
{
mysql_close(FMySQL);
}
void CMySQL::ResetMySQL()
{
mysql_ping(FMySQL);
}
//---------------------------------------------------------------------------
bool CMySQL::DatabaseExist(const char* db_name)
{
bool found = false;
FMySQL_Result = mysql_list_dbs(FMySQL, NULL);
while((FMySQL_Row = mysql_fetch_row(FMySQL_Result)) != NULL)
if (stricmp(db_name, *FMySQL_Row) == 0)
{
found = true;
break;
}
mysql_free_result(FMySQL_Result);
return found;
}
void CMySQL::CreateDatabase(const char* db_name)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "CREATE DATABASE %s", db_name);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}
void CMySQL::SelectDatabase(const char* db_name)
{
mysql_select_db(FMySQL, db_name);
}
void CMySQL::DeleteDatabase(const char* db_name)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "DROP DATABASE %s", db_name);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}
char* CMySQL::GetCurrSelectDatabaseName()
{
return FMySQL->db;
}

//---------------------------------------------------------------------------
unsigned int CMySQL::GetTableCount(const char* db_name)
{
unsigned int result = 0;
mysql_select_db(FMySQL, db_name);
FMySQL_Result = mysql_list_tables(FMySQL, NULL);
if (FMySQL_Result != NULL)
result = (unsigned int)FMySQL_Result->row_count;
mysql_free_result(FMySQL_Result);
return result;
}
void CMySQL::DeleteTable(const char* tb_name)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "DROP TABLE IF EXISTS %s", tb_name);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}
void CMySQL::OptimizeTable(const char* tb_name)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "OPTIMIZE TABLE %s", tb_name);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}

void CMySQL::RenameTable(const char* old_tbname, const char* new_tbname)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "ALTER TABLE %s RENAME %s", old_tbname, new_tbname);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}
void CMySQL::EmptyTable(const char* tb_name)
{
char* sql_express = new char[MAX_SQL_SIZE];
memset(sql_express, '/0', MAX_SQL_SIZE);
sprintf(sql_express, "DELETE FROM %s", tb_name);
mysql_real_query(FMySQL, sql_express, strlen(sql_express));
delete[] sql_express;
}

//---------------------------------------------------------------------------
int CMySQL::ExecSQL(const char* sql_express)
{
ResetQueryParams();
return mysql_real_query(FMySQL, sql_express, strlen(sql_express));
}

unsigned int CMySQL::GetAffectedRows()
{
return (unsigned int)mysql_affected_rows(FMySQL);
}
void CMySQL::OpenQuery()
{
FMySQL_Result = mysql_store_result(FMySQL);
FMySQL_FieldCount = mysql_num_fields(FMySQL_Result);
FMySQL_Fields = mysql_fetch_fields(FMySQL_Result);
FMySQL_Row = mysql_fetch_row(FMySQL_Result);
FMySQL_RowOffset = 0;
}
void CMySQL::CloseQuery()
{
mysql_free_result(FMySQL_Result);
ResetQueryParams();
}
unsigned int CMySQL::GetRecordCount()
{
if (FMySQL_Result != NULL)
return (unsigned int)FMySQL_Result->row_count ;
return 0;
}
bool CMySQL::IsEof()
{
return FMySQL_Row == NULL;
}
bool CMySQL::IsBof()
{
return FMySQL_Row == NULL;
}
void CMySQL::Next()
{
if (FMySQL_RowOffset <= FMySQL_Result->row_count)
{
FMySQL_Row = mysql_fetch_row(FMySQL_Result);
FMySQL_RowOffset++;
}
}
void CMySQL::Prev()
{
if (FMySQL_RowOffset >= 0)
{
FMySQL_RowOffset--;
mysql_data_seek(FMySQL_Result, FMySQL_RowOffset);
FMySQL_Row = mysql_fetch_row(FMySQL_Result);
}
}
unsigned int CMySQL::GetFieldCount()
{
return FMySQL_FieldCount;
}
char* CMySQL::GetFieldName(int field_index)
{
if (FMySQL_Fields != NULL)
return FMySQL_Fields[field_index].name;
return NULL;
}
bool CMySQL::FieldExists(const char* fd_name)
{
char* field;
for(int i = 0;
i < FMySQL_FieldCount;
i++)
{
field = FMySQL_Fields.name;
if (stricmp(field, fd_name) == 0)
return true;
}
return false;
}
int CMySQL::GetFieldType(const char* fd_name)
{
char* field;
for(int i = 0;
i < FMySQL_FieldCount;
i++)
{
field = FMySQL_Fields.name;
if (stricmp(field, fd_name) == 0)
return FMySQL_Fields.type;
}
return 0;
}

//---------------------------------------------------------------------------
char* CMySQL::FetchFieldAsBlob(const char* fd_name)
{
if (FMySQL_Row != NULL)
{
char* field;
for(int i = 0;
i < FMySQL_FieldCount;
i++)
{
field = FMySQL_Fields.name;
if (stricmp(field, fd_name) == 0)
return FMySQL_Row;
}
}
return NULL;
}
char* CMySQL::FetchFieldAsString(const char* fd_name)
{
return FetchFieldAsBlob(fd_name);
}
int CMySQL::FetchFieldAsInteger(const char* fd_name)
{
return my_strtol(FetchFieldAsString(fd_name));
}
bool CMySQL::FetchFieldAsBool(const char* fd_name)
{
return FetchFieldAsInteger(fd_name) != 0;
}
double CMySQL::FetchFieldAsFloat(const char* fd_name)
{
return my_strtod(FetchFieldAsString(fd_name));
}
//---------------------------------------------------------------------------
 
为什么需要自己来写一套?
jdbc, odbc, ado这些不都是为了统一数据库访问而诞生的吗?
 
to Adnil:
请问:你会用 ADO 去访问 IB 或者 MySQL 吗?
我的目标就是:在应用本身,不要直接去和数据库打交道,
而是通过一个中介代理,这样既可以达到更快速的访问,又
能够在应用中摆脱数据访问的困挠。主动权已经掌握在你手
中,象 DELPHI 的 IB/MySQL 组件,它的消耗明显要高于我代码的实现!
 
IB我不清楚,MySQL可以用ADO来访问的。
我的意思是,假设你现在的系统使用了mysql,并且使用了你自己开发的mysql数据代理组件,
使用中也很理想,没有问题。
但随着系统的慢慢庞大和业务的扩展,发现使用大型数据库Oracle更加适合,这时候就
会比较麻烦了,是否会再写一个Oracle数据代理组件呢?
假设我使用的是ado,只要数据库提供了ado驱动,我在更新数据库的时候,只需要更改
应用程序中的ado连接字符串即可,开发效率会高出很多。
 
to Adnil:
你说得很对,但有一点,你没有注意:
ADO/COM 是 MS-WINDOWS 专有的的。
目前而言,一个 ADO 组件可以使你事
半功倍。但如果你的数据库服务器不是 MS-WINDOWS
呢,比如是用:Linux?你还能用 ADO 吗?
你所有的程序可能都要重写。而我只要改变一
下这个 DLL 接口(应该是很小的接口变动,如果考虑好的
话(指设计接口时已经考虑数据库本身的UNIX接口),
甚至你只要改变一下相关的连接如我的MySQL 中,本来应该用
ConnectMySQL,现在改为ConnectMySQL2,就可以了),你说
哪个代价大?
 
我认为既然用C++Builder在Windows平台上面开发,那把数据库服务器架构在Linux上的可能是
比较小的。 就算数据库放在Linux、Unix上,通常的数据库如Oracle等还是可以用ADO连接上的。
另外,如果自己写统一数据访问,C++Builder中的所有数据敏感组件都将不可使用,这也是一个
损失。 ADO/BDE等实现的事务机制也将无法使用,如果自己实现的话难度会比较高。
 
多人接受答案了。
 
后退
顶部