这是一个神奇的开发接口!
它支持mysql, sqlite, 达梦和oracle这四款数据库的访问!
它对外提供统一的C++抽象接口,隐藏内部具体的实现细节,对于使用者学习成本为0!
它提供抽象的结果表对象(result_table)和结果集对象(result_set),满足不同使用者的使用习惯!
它目前只能工作在Linux(x86_64)平台下,还不支持跨平台,还在继续开发和维护中。
好了,有没有引起你的一丢丢好奇呢?下面我们就来掀开它那神奇的面纱:
/*
* 文件名:
* db_client.h
*
* 作 用:
* 数据库操作接口(支持国产达梦数据库, mysql数据库, sqlite数据库和oracle数据库)
*
* 介 绍:
* 1. 封装了达梦数据库的DPI接口;
* 2. 封装了mysql数据库的C接口;
* 3. 封装了sqlite数据库的sqlite3接口;
* 4. 封装了oracle数据库的oci(使用ocilib)接口;
* 5. 支持连接断线自动重连(mysql和达梦. oracle使用的是ocilib提供的pool,不清楚是否提供断线自动重连);
* 6. 提供数据库连接池功能(sqlite不提供此功能,但接口与其他数据库一致);
* 7. 向用户层提供统一的c++访问接口, 隐藏具体的数据库细节;
* 8. 提供常用的数据类型访问(TEXT(CHAR,VARCHAR,NVARCHAR,TEXT), SHORT(2), INT(4), LONG(8), FLOAT, DOUBLE, TIMESTAMP和BLOB等基础类型操作);
* 9. 提供抽象的结果表对象(result_table)和结果集对象(result_set),满足不同的使用习惯;
*
* 作 者
* shenyi(E-mail: shenyi0106@163.com QQ: 52851771)
*
* 时 间:
* 2019/11/25
*
* 依 赖:
* libdmdpi.so(达梦DPI接口库)
* libmysqlclient.so
* libsqlite3.so
* libocilib.so(ORACLE, 依赖OCI接口库)
*
* 版 权:
*
*/
#ifndef DB_CLIENT_H
#define DB_CLIENT_H
#include <string>
#include <vector>
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
#ifndef MIN
#define MIN(x,y) (x) > (y) ? (y) : (x)
#endif
#ifndef MAX
#define MAX(x,y) (x) > (y) ? (x) : (y)
#endif
//
// 支持的底层数据库接口
//
enum DB_CLIENT_TYPE
{
DB_CLIENT_TYPE_DAMO = 1, // 达梦
DB_CLIENT_TYPE_MYSQL = 2, // MYSQL
DB_CLIENT_TYPE_SQLITE = 3, // SQLITE3
DB_CLIENT_TYPE_ORACLE = 4, // ORACLE(OCI)
};
//
// 支持的数据类型(目前访问接口只支持以下几种数据类型)
//
enum DATA_TYPE
{
DATA_CTYPE_NULL = -1,
DATA_CTYPE_STRING = 0,
DATA_CTYPE_SSHORT = 1,
DATA_CTYPE_USHORT = 2,
DATA_CTYPE_SLONG = 3,
DATA_CTYPE_ULONG = 4,
DATA_CTYPE_FLOAT = 5,
DATA_CTYPE_DOUBLE = 6,
DATA_CTYPE_BLOB = 12,
DATA_CTYPE_SINT = 13,
DATA_CTYPE_UINT = 14,
DATA_CTYPE_TIMESTAMP = 15,
};
//
// 日志级别
// 数字越高,级别越高; 较低的日志级别可以包含较高的日志级别的日志,反之却不行.
// 例如:
// 1. 设置了DBG_LEVEL_DEBUG, 系统大于等于DBG_LEVEL_DEBUG的日志都可以被打印出来(全部日志);
// 2. 设置了DBG_LEVEL_WARN, 系统只能打印DBG_LEVEL_WARN和DBG_LEVEL_ERROR这两个级别的日志;
//
enum DBG_LEVEL
{
DBG_LEVEL_DEBUG = 1,
DBG_LEVEL_INFO = 4,
DBG_LEVEL_WARN = 8,
DBG_LEVEL_ERROR = 16,
};
//
// 缺省数据库连接和执行超时时间
//
#define DEFAULT_TIMEOUT_SECOND 2
//
// 使用stmt模式下,可用的最大参数数量
//
#define MAX_BIND_PARAM_COUNT 64
//
// stmt模式下的参数结构
//
struct bind_param
{
#if SUPPORT_ORACLE
char name[64]; // 参数名称(表示参数名称,ocilib不支持按索引绑定参数模式)
void *lob; // 内部函数中使用,调用者请忽略此参数
#endif
void *buffer; // 指向缓冲区的指针
long buf_type; // 参数类型, DATA_TYPE中的一个
unsigned long buf_size; // 缓冲区大小
unsigned long buf_len; // 参数的长度
};
//
// 结果集内存表
// 将查询结果在内存中构建成一个schema, 可以像二维表格一样随机访问任意一个单元数据
//
class result_table
{
public:
//
// 获得纪录集行数
//
virtual int get_row_count() = 0;
//
// 获得纪录集列数量
//
virtual int get_col_count() = 0;
//
// 读取字段的string值
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_string_value(int row, int col, std::string &val) = 0;
virtual bool get_string_value(int row, std::string name, std::string &val) = 0;
//
// 读取字段的string值(通过返回值的形式)
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual std::string get_string_value(int row, int col) = 0;
virtual std::string get_string_value(int row, std::string name) = 0;
//
// 读取字段的short值(16位)
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_short_value(int row, int col, short &val) = 0;
virtual bool get_short_value(int row, std::string name, short &val) = 0;
//
// 读取字段的int值(32位)
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_int_value(int row, int col, int &val) = 0;
virtual bool get_int_value(int row, std::string name, int &val) = 0;
//
// 读取字段的long值(64位)
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_long_value(int row, int col, long &val) = 0;
virtual bool get_long_value(int row, std::string name, long &val) = 0;
//
// 读取字段的float值
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_float_value(int row, int col, float &val) = 0;
virtual bool get_float_value(int row, std::string name, float &val) = 0;
//
// 读取字段的double值
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
virtual bool get_double_value(int row, int col, double &val) = 0;
virtual bool get_double_value(int row, std::string name, double &val) = 0;
//
// 读取字段的二进制内容
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
// val : 输出参数, 指向二进制缓冲区
// ret_len: 输出参数, 二进制缓冲区长度
//
//
virtual bool get_blob_value(int row, int col, void **val, int *ret_len) = 0;
virtual bool get_blob_value(int row, std::string name, void **val, int *ret_len) = 0;
//
// 读取字段的日期时间
// col : 基于0的列索引
// name: 列名称(不区分大小写)
//
// tv : 输出参数, 日期结构
//
virtual bool get_datetime_value(int row, int col, struct tm *tv) = 0;
virtual bool get_datetime_value(int row, std::string name, struct tm *tv) = 0;
};
//
// 结果集操作接口
// 可通过此接口来顺序获取各行和各列的数据
//
class result_set
{
public:
//
// 读取下一行
//
virtual bool fetch_row() = 0;
//
// 获得纪录集行数
//
// 注意:
// 达梦数据库此接口无效! 仅当fetch_row到最后一行时,此接口才有效.
// SQLITE此接口无效!
//
virtual int get_row_count() = 0;
//
// 获得纪录集列数量
//
virtual int get_col_count() = 0;
//
// 读取字段值数据(基础接口)
//
// ctype : 输入参数, 读取的字段类型; DATA_TYPE中的一个;
// val : 输出参数, 读取字段内容将存放到缓冲区中
// buf_len: 输入参数, 缓冲区val的大小
// ret_len: 输出参数, 字段内容大小(可以为nullptr,表示不关心返回长度)
//
// 备注:
// 当val==nullptr且buf_len==0, 而ret_len!=nullptr, 则返回所读字段的长度;
//
virtual bool get_value(int col, int ctype, void *val, int buf_len, int *ret_len) = 0;
//
// 读取字段的字符串值
//
// val: 输出参数
//
virtual bool get_string_value(int col, std::string &val) = 0;
//
// 读取字段的字符串值(通过返回值形式)
//
virtual std::string get_string_value(int col) = 0;
//
// 读取字段的short值(16位)
//
// val: 输出参数
//
virtual bool get_short_value(int col, short &val) = 0;
//
// 读取字段的int值(32位)
//
// val: 输出参数
//
virtual bool get_int_value(int col, int &val) = 0;
//
// 读取字段的long值(64位)
//
// val: 输出参数
//
virtual bool get_long_value(int col, long &val) = 0;
//
// 读取字段的float值
//
// val: 输出参数
//
virtual bool get_float_value(int col, float &val) = 0;
//
// 读取字段的double值
//
// val: 输出参数
//
virtual bool get_double_value(int col, double &val) = 0;
//
// 读取字段的二进制数据
//
// val : 输出参数, 存放二进制数据的缓冲区
// buf_len: 输入参数, 缓冲区val的大小
// ret_len: 输出参数, 读取的二进制数据的长度
//
virtual bool get_blob_value(int col, void *val, int buf_len, int *ret_len) = 0;
//
// 读取字段的日期时间值
//
// val: 输出参数
//
virtual bool get_datetime_value(int col, struct tm *val) = 0;
};
//
// 数据库连接
// 表示一条数据库连接, 通过此对象可以执行数据的增删改查操作;
// 所有的execute_*接口都是线程安全的
//
class connection
{
public:
//
// 释放连接到连接池
// 作用等同于connect_pool::close()
//
virtual void close() = 0;
//
// 查询数据
// 返回结果集操作对象
//
// 备注:
// 请使用free_result_set接口释放结果集操作对象,否则会导致严重的内存泄露
//
virtual result_set* execute_query(const char *sql, ...) = 0;
virtual result_set* execute_query(std::string sql) = 0;
virtual void free_result_set(result_set **result) = 0;
//
// 查询数据
// 返回结果集内存表
//
// 备注:
// 1. 请使用free_data_table接口释放结果集内存,否则会导致严重的内存泄露;
// 2. 此接口会依据结果集操作对象(result_set),在内存中构建相关schema数据结构,
// 所以请不要在查询大数据集时使用此接口,否则可能导致无可用内存的情况出现;
// 3. 推荐使用环境: 在查询数据集<=1000条, 使用此接口
//
virtual result_table* execute_query2(const char *sql, ...) = 0;
virtual result_table* execute_query2(std::string sql) = 0;
virtual void free_data_table(result_table **table) = 0;
//
// 执行INSERT, DELETE, UPDATE语句接口
//
virtual bool execute_update(const char *sql, ...) = 0;
virtual bool execute_update(std::string sql) = 0;
virtual bool execute_update(std::vector<std::string> sqls) = 0;
//
// 通过stmt模式,绑定参数执行数据更新操作
// param : 参数集合(param[])
// size : 参数集合param数量
//
virtual bool execute_bind(const char *sql, struct bind_param *param, int size) = 0;
//
// 连接是否有效
//
// 备注:
// 此接口无效(保留以后使用)
//
virtual bool is_open() = 0;
//
// 事务相关
//
// 备注:
// 以下三个接口对sqlite无效!
//
virtual bool set_auto_commit(bool enable) = 0;
virtual bool commit() = 0;
virtual bool rollback() = 0;
};
//
// 数据库连接池
// 初始化一定数量的数据库连接保持在系统中,可以提高数据库访问效率
//
class connection_pool
{
public:
//
// 作用等同于connect中的同名函数
// 目的是减少应用层申请和释放连接的步骤
//
virtual result_set* execute_query(const char *sql, ...) = 0;
virtual result_set* execute_query(std::string sql) = 0;
virtual void free_result_set(result_set **result) = 0;
//
// 作用等同于connect中的同名函数
// 目的是减少应用层申请和释放连接的步骤
//
virtual result_table* execute_query2(const char *sql, ...) = 0;
virtual result_table* execute_query2(std::string sql) = 0;
virtual void free_data_table(result_table **table) = 0;
//
// 作用等同于connect中的同名函数
// 目的是减少应用层申请和释放连接的步骤
//
virtual bool execute_update(const char *sql, ...) = 0;
virtual bool execute_update(std::string sql) = 0;
virtual bool execute_update(std::vector<std::string> sqls) = 0;
//
// 作用等同于connect中的同名函数
// 目的是减少应用层申请和释放连接的步骤
//
virtual bool execute_bind(const char *sql, struct bind_param *param, int size) = 0;
//
// 连接池类型
// DB_CLIENT_TYPE枚举中的一个
//
virtual int dbtype() = 0;
//
// 从连接池中获取一个连接对象
//
// 返回值:
// 成功返回 connect对象; 失败返回nullptr;
//
// 备注:
// 当连接对象使用完后, 必须要使用close接口释放连接对象,
// 否则会造成连接池耗尽,无连接可用.
//
virtual connection* open() = 0;
//
// 释放一个连接对象到连接池中
//
// conn: 连接对象
//
virtual void close(connection *conn) = 0;
};
//
// 本地导出接口
//
extern "C"
{
/*
功 能:
设置接口库中日志的打印级别
参 数:
level: 日志级别, DBG_LEVEL中的一个;
返回值:
无
备 注:
接口库的默认日志级别是DBG_LEVEL_DEBUG;
*/
void db_client_set_debug(int level);
/*
功 能:
创建创建数据库连接池对象
参 数:
dbtype : 数据库类型, DB_CLIENT_TYPE中的一个;
host : 数据库访问地址(IP形式);
port : 数据库的访问端口;
name : 要创建的数据库名称(必须是英文名);
user : 访问数据库的用户名
passwd : 访问数据库的密码
pool_num: 指定连接池中连接的数量
返回值:
成功返回连接池对象, 失败返回nullptr
备 注:
1. 当前只支持DB_CLIENT_TYPE中所列的数据库;
2. 连接池不再使用时,请使用db_client_destory_pool释放对象;
3. 此连接池中的连接数量为"一次成型", 即创建连接池时一并把指定的连接数量创建完毕,
使用过程中,不会根据业务请求量自动添加或者删除连接;
4. 参数使用介绍:
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| dbtype | host | port | name | user | passwd | pool_num |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_DAMO | IP地址 | 端口 | 忽略 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_MYSQL | IP地址 | 端口 | 数据库名称 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_SQLITE | 文件全路径 | 忽略 | 忽略 | 忽略 | 忽略 | 忽略 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_ORACLE | TNS名称 | 忽略 | 忽略 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
*/
connection_pool* db_client_create_pool(int dbtype, const char *host, int port, const char *name,
const char *user, const char *passwd, int pool_num);
/*
功能:
一组db_client_create_pool相关的宏定义, 用于简化创建数据库连接池的步骤.
备注:
具体参数用法请参照db_client_create_pool函数中有关"参数使用介绍"章节
*/
#define db_client_create_pool_mysql(host, port, name, user, passwd, pool) db_client_create_pool(DB_CLIENT_TYPE_MYSQL, host, port, name, user, passwd, pool)
#define db_client_create_pool_damo(host, port, user, passwd, pool) db_client_create_pool(DB_CLIENT_TYPE_DAMO, host, port, nullptr, user, passwd, pool)
#define db_client_create_pool_sqlite(path) db_client_create_pool(DB_CLIENT_TYPE_SQLITE, path, 0, nullptr, nullptr, nullptr, 0 )
#define db_client_create_pool_oracle(host, user, passwd, pool) db_client_create_pool(DB_CLIENT_TYPE_ORACLE, host, 0, nullptr, user, passwd, pool)
/*
功 能:
释放数据库连接池对象
参 数:
pool : 数据库连接池对象
返回值:
返回0(忽略返回值)
备 注:
请参考db_client_create_pool接口说明.
*/
int db_client_destory_pool(connection_pool *pool);
}
#endif // DB_CLIENT_H
它导出三个重要的C接口,这三个接口是这个接口库的入口,更是它的灵魂。下面我们来分别介绍着三个重要的接口:
/*
功 能:
设置接口库中日志的打印级别
参 数:
level: 日志级别, DBG_LEVEL中的一个;
返回值:
无
备 注:
接口库的默认日志级别是DBG_LEVEL_DEBUG;
*/
void db_client_set_debug(int level);
此接口用来设置开发库中能够输出的日志级别。
libdbclient开发库中添加了许多日志调试信息,使用了不同的日志级别来控制,可以通过这个接口设置需要输出的日志级别,来控制开发接口输出你所需要的调试信息。开发接口默认的日志级别是DBG_LEVEL_DEBUG,也就是说,它会将所有日志都输出出来。
/*
功 能:
创建创建数据库连接池对象
参 数:
dbtype : 数据库类型, DB_CLIENT_TYPE中的一个;
host : 数据库访问地址(IP形式);
port : 数据库的访问端口;
name : 要创建的数据库名称(必须是英文名);
user : 访问数据库的用户名
passwd : 访问数据库的密码
pool_num: 指定连接池中连接的数量
返回值:
成功返回连接池对象, 失败返回nullptr
备 注:
1. 当前只支持DB_CLIENT_TYPE中所列的数据库;
2. 连接池不再使用时,请使用db_client_destory_pool释放对象;
3. 此连接池中的连接数量为"一次成型", 即创建连接池时一并把指定的连接数量创建完毕,
使用过程中,不会根据业务请求量自动添加或者删除连接;
4. 参数使用介绍:
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| dbtype | host | port | name | user | passwd | pool_num |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_DAMO | IP地址 | 端口 | 忽略 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_MYSQL | IP地址 | 端口 | 数据库名称 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_SQLITE | 文件全路径 | 忽略 | 忽略 | 忽略 | 忽略 | 忽略 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
| DB_CLIENT_TYPE_ORACLE | TNS名称 | 忽略 | 忽略 | 用户名 | 密码 | 最大连接数 |
+-----------------------+-------------+-------------+-------------+-------------+-------------+-------------+
*/
connection_pool* db_client_create_pool(int dbtype, const char *host, int port, const char *name,
const char *user, const char *passwd, int pool_num);
db_client_create_pool这个接口是三个接口中的“重中之重”,一切数据库的访问操作,都必须从它开始。
具体的参数说明和使用介绍,在接口文件中已经写的非常详细了,这里就不再复述了。需要说明的是,由于要兼容不同的数据库访问,不同的数据库访问又需要不同的参数,所以这个接口的参数并不是都有用,需要根据dbtype参数来区别对待,访问何种数据库,请参照说明,匹配不同的参数。
另外,还需要说明的是,关于连接池这个功能。 这个开发接口实现的连接池是非常简单的连接池,不提供额外的动态关闭和释放连接的功能,也不会根据业务量的变化动态管理这个连接池。你初始化了多少个连接,它就一直帮你管理这些个连接,不会自作主张的替你考虑更多的事情。
可能你已经看到了,对于sqlite类型,pool_num这个参数是0(其实不光是pool_num,其他参数也被忽略了),它不需要这个参数,因为sqlite就是一个本地文件,它无需连接池。
/*
功 能:
释放数据库连接池对象
参 数:
pool : 数据库连接池对象
返回值:
返回0(忽略返回值)
备 注:
请参考db_client_create_pool接口说明.
*/
int db_client_destory_pool(connection_pool *pool);
这最后一个接口就无需做过多的介绍了,有分配就有释放,这个是搞C/C++开发的座右铭,如果你不知道这个座右铭,那么你一定不是搞C/C++开发的。
导出函数就介绍到这里了,很简单吧,就三个接口,使用起来是不是很简单,0学习成本?
有关其他的C++抽象接口,可以直接看接口文件,里面都有详细的说明,而且通过函数的名字也能够一目了然的知道它是干什么的,这里就不介绍了。
下载链接:点我