学习实践:使用模式,原则实现一个C++数据库访问类

本文介绍了如何使用设计模式和原则实现一个C++数据库访问类,详细讨论了接口设计,包括接口概述、具体接口及其使用案例。同时,文章深入探讨了实现细节,如可变参数处理和互斥锁的运用,以及HiDBImpl和HiDB的实现过程。最后,作者分享了一些后记和总结。
摘要由CSDN通过智能技术生成

一、概述

在我参与的多个项目中,大家使用libMySQL操作MySQL数据库,而且是源码级复用,在多个项目中相同或相似的源码,这样的复用方式给开发带来了不便。
libMySQL的使用比较麻烦,很容易出错。
基于以上原因,我写了一个动态链接库,将对libMySQL的操作封装起来,以二进制复用代替源码级复用;要提供线程安全的接口,用户无需关系是否加锁这样细节性的问题,减少出错及死锁的机会,当然也要允许用户自己选择是否线程安全的访问数据库;要简化访问数据库的流程,接口越简单越好。

我从2011年开始写这个库,我给它起名字叫HiDB。HiDB从2011年到现在,经历了一个由简到繁又有繁到简的过程,中间也包含了自己对程序理解的变化。
很多的考虑看起来很细碎,例如代码的注释风格(选择我们一贯的注释风格还是doxygen的注释风格,代码大全中也讲到了注释风格。是否需要借鉴);代码的命名规范(匈牙利命名法还是Java,C#的命名法,还有很多开源项目的命名规范);C++设计新思维中讲到Policy classes,是否可以应用到这个库中;Effective C++中讲的接口与实现分离。
这些年自己考虑过的一些东西包括注释风格的选择(我们一贯的注释,doxygen的注释风格,代码大全中也讲到了注释风格),出错时是使用错误码报出错误还是使用异常报出错误?C++设计新思维中讲的Policy classes,Effective C++中讲的接口与实现分析(Impl)

二、接口

(一)接口概述

首先确定一下HiDB的接口。该库对外显示为一个HiDB类,该类应该包含的接口包括:
1: 用户选择线程安全还是非线程安全
2: 打开数据库连接
3: 关闭数据库连接
4: 执行insert,update等非查询操作的接口(ExecuteNoQuery)
5: 获得查询结果第一行第一列的接口(ExecuteScaler)
6: 获得一次查询的所有记录(多行多列)的接口(ExecuteQuery)
7: 执行事务的接口(OnTransaction)

所有的接口都在产生异常时抛出,需要对异常做一个封装,这个异常中最好能标识出异常产生的位置,异常的编号,异常的描述,引起异常的SQL语句。我设计的异常如下:

   
   
   
/** @brief 数据库操作异常 */
class HI_DB_EXPORT HiDBException
{
public:
HiDBException();
public:
std::string ToSrting();
public:
std::string m_sql; /**< 本次操作的SQL语句 */
std::string m_descript; /**< 异常描述 */
std::string m_position; /**< 异常位置 */
long m_errorId; /**< 异常编号 */
HiDBType m_dbTyp; /**< 数据库类型 */
};


为了方便的抛出异常,我提供了一个宏(这个宏只有HiDB库使用)
   
   
   
/** @brief 异常语句宏 */
#define HiDBHelperOnError(ps, script,sql, id) \
HiDBException exception;\
exception.m_position = ps;\
exception.m_descript = script;\
exception.m_sql = sql;\
exception.m_errorId = id;\
throw exception;\
//return false;

提供该宏,除了简化用户输入外,还有一个想法就是如果用户不想使用异常,可以修改该宏,例如返回false。

熟悉ADO.NET的朋友应该可以看出这些接口是借鉴了ADO.NET的。

(二)具体接口

<1> 构造函数
本来在《C++设计新思维》中,有个Policy classes,适合这种根据用户需要提供安全或非安全接口的需求,但是Policy classes适合模板,不适合非模板,所以在这儿就没有使用,只是在构造函数中添加了一个布尔参数isUsingLock,如果为true则提供线程安全接口,否则接口是非线程安全的。
HiDB打算在将来除支持MySQL外,还支持其他数据库,所以在构造函数中,除isUsingLock外,还有一个选择数据库类型的接口。
将数据库类型写成枚举,则枚举为:

   
   
   
/** @brief 数据库类型 */
enum HiDBType
{
HiDBType_Invail, /**< 无效类型 */
HiDBType_MySQL, /**< MySQL */
};


构造函数的声明就明确下来了:
 
  
  
  
/**
* @brief 构造函数
* @param[in] type 数据库类型
* @param[in] isUsingLock 是否需要使用互斥锁
*/
HiDB(HiDBType type = HiDBType_MySQL, bool isUsingLock = false);

<2>打开数据库连接
打开数据库连接比较简单:

   
   
   
/**
* @brief 打开数据库连接
* @param[in] conn 数据库连接字符串
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"))
* {
* // 打开成功
* }
* else
* {
* // 打开失败
* }
* @endcode
*/
bool Open(const char* conn) throw (HiDBException);

该接口的conn参数是一个字符串,这样字符串就具有扩展性,可以针对不同的数据库,进行不同的处理(这一点感觉不是很好,但是能提供接口的稳定性)。不同数据库,需要满足特定的格式,在MySQL中,要使用类似于“host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;”的格式。
<3> 关闭数据库连接
   
   
   
/**
* @brief 关闭据库连接
*/
void Close(void);

<4> IsOpen接口
这个接口是延伸出来的,既然有open,close,提供一个IsOpen好像也是应该的,让用户了解当前的打开状态。

   
   
   
/**
* @brief 数据库连接是否打开
*/
bool IsOpen();

<5> 执行非查询语句接口
执行SQL语句的接口,应该可以接收可变参数,除可变参数外,还应该有一个包含sql语句的字符串参数,所以接口定义如下:

    
    
    
/**
* @brief 执行SQL语句,并不返回结果
* @param[in] conn SQL语句
* @retval true:成功,false;失败
* @par 实例:
* @code
* HiDB db;
* if (db.ExecuteNoQuery("UPDATE table SET Paramer1='%s'
* and Paramer2='%s' OR Paramer3=%d", "test1", "test2", 3))
* {
* // 执行成功
* }
* else
* {
* // 执行失败
* }
* @endcode
*/
bool ExecuteNoQuery(const char* sql, ...) throw (HiDBException);

<6>  获得查询结果第一行第一列的接口
该接口的参数与执行非查询语句的接口一致,但是返回值应该为字符串,如果执行失败,则应该返回空字符串。触发异常时,抛出 HiDBException异常。

   
   
   
/**
* @brief 执行SQL语句,返回一个结果
* @param[in] sql SQL语句
* @retval 获得的数据,如果为空,则失败
*/
std::string ExecuteScalar(const char* sql, ...) throw (HiDBException);

<7>  获得一次查询的所有记录(多行多列)的接口
该接口的参数与执行非查询语句的接口一致。
返回结果应该是包含多行多列的一个数据集,在ADO.NET中有DataTable,在这儿,我们可以用stl中的vector存储多行,map存储每行数据的多列。
多以需要定义一个数据集:
   
   
   
#ifndef HiDBTable
typedef std::map<std::string, std::string> HiDBMap;
/** @brief 查询结果 */
typedef std::
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值