一、概述
在我参与的多个项目中,大家使用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语句。我设计的异常如下:
 
  1. /** @brief 数据库操作异常 */
  2. class HI_DB_EXPORT HiDBException
  3. {
  4. public:
  5. HiDBException();
  6. public:
  7. std::string ToSrting();
  8. public:
  9. std::string m_sql; /**< 本次操作的SQL语句 */
  10. std::string m_descript; /**< 异常描述 */
  11. std::string m_position; /**< 异常位置 */
  12. long m_errorId; /**< 异常编号 */
  13. HiDBType m_dbTyp; /**< 数据库类型 */
  14. };
 
 
为了方便的抛出异常,我提供了一个宏(这个宏只有HiDB库使用)
  1. /** @brief 异常语句宏 */
  2. #define HiDBHelperOnError(ps, script,sql, id) \
  3. HiDBException exception;\
  4. exception.m_position = ps;\
  5. exception.m_descript = script;\
  6. exception.m_sql = sql;\
  7. exception.m_errorId = id;\
  8. throw exception;\
  9. //return false;
 
 
提供该宏,除了简化用户输入外,还有一个想法就是如果用户不想使用异常,可以修改该宏,例如返回false。
 
熟悉ADO.NET的朋友应该可以看出这些接口是借鉴了ADO.NET的。
(二)具体接口
<1> 构造函数
本来在《C++设计新思维》中,有个Policy classes,适合这种根据用户需要提供安全或非安全接口的需求,但是Policy classes适合模板,不适合非模板,所以在这儿就没有使用,只是在构造函数中添加了一个布尔参数isUsingLock,如果为true则提供线程安全接口,否则接口是非线程安全的。
HiDB打算在将来除支持MySQL外,还支持其他数据库,所以在构造函数中,除isUsingLock外,还有一个选择数据库类型的接口。
将数据库类型写成枚举,则枚举为:
 
  1. /** @brief 数据库类型 */
  2. enum HiDBType
  3. {
  4. HiDBType_Invail, /**< 无效类型 */
  5. HiDBType_MySQL, /**< MySQL */
  6. };
  7.  
  8. 构造函数的声明就明确下来了:
  9.  
  10. /**
  11. * @brief 构造函数
  12. * @param[in] type 数据库类型
  13. * @param[in] isUsingLock 是否需要使用互斥锁
  14. */
  15. HiDB(HiDBType type = HiDBType_MySQL, bool isUsingLock = false);
  16.  
  17. <>打开数据库连接
  18. 打开数据库连接比较简单:
  19.  
  20. /**
  21. * @brief 打开数据库连接
  22. * @param[in] conn 数据库连接字符串
  23. * @retval true:成功,false;失败
  24. * @par 实例:
  25. * @code
  26. * HiDB db;
  27. * if (db.Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"))
  28. * {
  29. * // 打开成功
  30. * }
  31. * else
  32. * {
  33. * // 打开失败
  34. * }
  35. * @endcode
  36. */
  37. 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> 关闭数据库连接
 
  1. /**
  2. * @brief 关闭据库连接
  3. */
  4. void Close(void);
 
<4> IsOpen接口
这个接口是延伸出来的,既然有open,close,提供一个IsOpen好像也是应该的,让用户了解当前的打开状态。

  1. /**
  2. * @brief 数据库连接是否打开
  3. */
  4. bool IsOpen();
 
 
<5> 执行非查询语句接口
执行SQL语句的接口,应该可以接收可变参数,除可变参数外,还应该有一个包含sql语句的字符串参数,所以接口定义如下:

  1. /**
  2. * @brief 执行SQL语句,并不返回结果
  3. * @param[in] conn SQL语句
  4. * @retval true:成功,false;失败
  5. * @par 实例:
  6. * @code
  7. * HiDB db;
  8. * if (db.ExecuteNoQuery("UPDATE table SET Paramer1='%s'
  9. * and Paramer2='%s' OR Paramer3=%d", "test1", "test2", 3))
  10. * {
  11. * // 执行成功
  12. * }
  13. * else
  14. * {
  15. * // 执行失败
  16. * }
  17. * @endcode
  18. */
  19. bool ExecuteNoQuery(const char* sql, ...) throw (HiDBException);
 
<6> 获得查询结果第一行第一列的接口
该接口的参数与执行非查询语句的接口一致,但是返回值应该为字符串,如果执行失败,则应该返回空字符串。触发异常时,抛出HiDBException异常。
 

  1. /**
  2. * @brief 执行SQL语句,返回一个结果
  3. * @param[in] sql SQL语句
  4. * @retval 获得的数据,如果为空,则失败
  5. */
  6. std::string ExecuteScalar(const char* sql, ...) throw (HiDBException);
 
<7> 获得一次查询的所有记录(多行多列)的接口
该接口的参数与执行非查询语句的接口一致。
返回结果应该是包含多行多列的一个数据集,在ADO.NET中有DataTable,在这儿,我们可以用stl中的vector存储多行,map存储每行数据的多列。
多以需要定义一个数据集:
  1. #ifndef HiDBTable
  2.  
  3. typedef std::map<std::string, std::string> HiDBMap;
  4.  
  5. /** @brief 查询结果 */
  6. typedef std::vector<HiDBMap> HiDBTable; /**< 查询结果 */
  7. #endif
 
 
因为HiDBTable中包含多个map,所以最好避免拷贝,使用stl的shared_ptr来避免多次拷贝:

  1. /**
  2. * @brief 执行SQL语句,返回一个结果集合
  3. * @param[in] sql SQL语句
  4. * @retval 存储查询记录的智能指针
  5. */
  6. std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql, ...) throw (HiDBException);
 
 
<8> 执行事务的接口
执行事务接口是一个Command模式的接口,参数应该是一个函数对象。该对象为无参无返回值的函数对象即可。stl中提供了function对象。(在最初的版本中是自己实现函数对象的)
 

  1. /**
  2. * @brief 在事务中执行处理
  3. * @param[in] fun 处理函数
  4. */
  5. void OnTransaction(const std::function<void()>& fun) throw (HiDBException);
 
(三) 接口的使用案例
  1. HiDB m_DB = new HiDB(HiDBType_MySQL, true);
  2. try
  3. {
  4. bool ret = m_DB->Open(
  5. "host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"
  6. );
  7. m_DB->ExecuteNoQuery("drop table if exists table1;");
  8. string val = m_DB->ExecuteScalar(
  9. "SELECT column4 FROM table1 WHERE column1='%s' AND column3=%d",
  10. &val, "hitest", );
  11. shared_ptr<HiDBTable> table = this->m_DB->ExecuteQuery(
  12. "SELECT * FROM table1 WHERE column1='%s' OR column1='%s'",
  13. "hitest", "mytest");
  14. }
  15. catch(HiDBException& e)
  16. {
  17. // ...
  18. }
 
(四) 其他
其实,我以前提供的接口比现在要复杂很多,首先我模仿ADO.NET,对SQL参数进行了封装,封装了SQL参数的名称,类型,是否为空,值等信息,对获得的数据也进行了类似的封装。 ,还针对SQL参数提供了Create和Delete函数。
有一个同时看了我的接口后说,我的接口太复杂了,分层不是明确。我接受了他的建议,就将接口修改为现在的接口了。
另外,执行事务接口,最开始我是自己创建函数对象的,这也增加了复杂度,后来使用了stl的function对象,辅以lambda表达式,则使用起来简单多了。
(五) 完整的接口:
<1>HiDBCommon.h 提供接口应用到的相关枚举和结构体
  1. #pragma once
  2.  
  3. /**
  4. * @defgroup 数据库模块
  5. * @{
  6. */
  7. #include "HiDBExport.h"
  8.  
  9. #include <string>
  10. #include <vector>
  11. #include <map>
  12. #include <sstream>
  13.  
  14. /** @brief 数据库类型 */
  15. enum HiDBType
  16. {
  17. HiDBType_Invail, /**< 无效类型 */
  18. HiDBType_MySQL, /**< MySQL */
  19. };
  20.  
  21. #ifndef HiDBTable
  22.  
  23. typedef std::map<std::string, std::string> HiDBMap;
  24.  
  25. /** @brief 查询结果 */
  26. typedef std::vector<HiDBMap> HiDBTable; /**< 查询结果 */
  27. #endif
  28.  
  29. /** @brief 数据库操作异常 */
  30. class HI_DB_EXPORT HiDBException
  31. {
  32. public:
  33. HiDBException();
  34. public:
  35. std::string ToSrting();
  36. public:
  37. std::string m_sql; /**< 本次操作的SQL语句 */
  38. std::string m_descript; /**< 异常描述 */
  39. std::string m_position; /**< 异常位置 */
  40. long m_errorId; /**< 异常编号 */
  41. HiDBType m_dbTyp; /**< 数据库类型 */
  42. };
  43.  
  44. /** @brief 异常语句宏 */
  45. #define HiDBHelperOnError(ps, script,sql, id) \
  46. HiDBException exception;\
  47. exception.m_position = ps;\
  48. exception.m_descript = script;\
  49. exception.m_sql = sql;\
  50. exception.m_errorId = id;\
  51. throw exception;\
  52. //return false;
  53.  
  54. /**//** @}*/ // 数据库模块
 
<2> HiDB.h 主要的接口:
  1. #pragma once
  2.  
  3. /**
  4. * @defgroup 数据库模块
  5. * @{
  6. */
  7.  
  8. #include <memory>
  9. #include <functional>
  10.  
  11. #include "HiDBCommon.h"
  12.  
  13. class HiDBImpl;
  14.  
  15. #pragma warning (disable: 4290)
  16.  
  17. /**
  18. * @brief 数据库操作类,封装数据库的通用操作,本类使用策略模式实现
  19. * @author 徐敏荣
  20. * @date 2012-06-14
  21. *
  22. * @par 修订历史
  23. * @version v0.5 \n
  24. * @author 徐敏荣
  25. * @date 2012-06-14
  26. * @li 初始版本
  27. * @version v0.6 \n
  28. * @author 徐敏荣
  29. * @date 2014-08-04
  30. * @li 简化程序
  31. *
  32. */
  33. class HI_DB_EXPORT HiDB
  34. {
  35. public:
  36.  
  37. /**
  38. * @brief 构造函数
  39. * @param[in] type 数据库类型
  40. * @param[in] isUsingLock 是否需要使用互斥锁
  41. */
  42. HiDB(HiDBType type = HiDBType_MySQL, bool isUsingLock = false);
  43.  
  44. /**
  45. * @brief 析构函数
  46. */
  47. ~HiDB();
  48.  
  49. public:
  50.  
  51. /**
  52. * @brief 打开数据库连接
  53. * @param[in] conn 数据库连接字符串
  54. * @retval true:成功,false;失败
  55. * @par 实例:
  56. * @code
  57. * HiDB db;
  58. * if (db.Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;"))
  59. * {
  60. * // 打开成功
  61. * }
  62. * else
  63. * {
  64. * // 打开失败
  65. * }
  66. * @endcode
  67. */
  68. bool Open(const char* conn) throw (HiDBException);
  69.  
  70. /**
  71. * @brief 关闭据库连接
  72. */
  73. void Close(void);
  74.  
  75. /**
  76. * @brief 数据库连接是否打开
  77. */
  78. bool IsOpen();
  79.  
  80. public:
  81.  
  82. /**
  83. * @brief 执行SQL语句,并不返回结果
  84. * @param[in] conn SQL语句
  85. * @retval true:成功,false;失败
  86. * @par 实例:
  87. * @code
  88. * HiDB db;
  89. * if (db.ExecuteNoQuery("UPDATE table SET Paramer1='%s'
  90. * and Paramer2='%s' OR Paramer3=%d", "test1", "test2", 3))
  91. * {
  92. * // 执行成功
  93. * }
  94. * else
  95. * {
  96. * // 执行失败
  97. * }
  98. * @endcode
  99. */
  100. bool ExecuteNoQuery(const char* sql, ...) throw (HiDBException);
  101.  
  102. public:
  103.  
  104. /**
  105. * @brief 执行SQL语句,返回一个结果
  106. * @param[in] sql SQL语句
  107. * @retval 获得的数据,如果为空,则失败
  108. */
  109. std::string ExecuteScalar(const char* sql, ...) throw (HiDBException);
  110.  
  111. public:
  112.  
  113. /**
  114. * @brief 执行SQL语句,返回一个结果集合
  115. * @param[in] sql SQL语句
  116. * @retval 存储查询记录的智能指针
  117. */
  118. std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql, ...) throw (HiDBException);
  119.  
  120. public:
  121.  
  122. /**
  123. * @brief 在事务中执行处理
  124. * @param[in] fun 处理函数
  125. */
  126. void OnTransaction(const std::function<void()>& fun) throw (HiDBException);
  127.  
  128. private:
  129. /**
  130. * @brief 数据库操作实现指针
  131. */
  132. HiDBImpl* m_Impl; /**< 数据库操作实现指针 */
  133. };
  134.  
  135. /**//** @}*/ // 数据库模块
 
三 实现
实现采用了从《Effective C++》中学习到的实现与接口相分析的原则,在HiDB中使用HiDBImpl实现访问数据库的逻辑。
(一) 可变参数的处理
当然,在HiDB中是需要解决根据sql参数和可变参数拼装成一个完整SQL语句的问题。
该问题使用一个宏来实现:
  1. #if !defined(HISDB_ON_VARLIST)
  2. #define HISDB_ON_VARLIST(x, y) \
  3. char chArr[] = {};\
  4. char* pchar = &chArr[];\
  5. va_list pArgList;\
  6. va_start(pArgList, y);\
  7. ::_vsnprintf(chArr, , x, pArgList); \
  8. va_end(pArgList) ;
  9. #endif
 
(二) 互斥锁的实现
自己根据临界区,实现了一个互斥锁,互斥锁接口如下:
1: 构造函数: 实现临界区的初始化
2: 析构函数: 实现临界区的删除
3: 进入临界区
4: 离开临界区
实现函数如下:

  1. /**
  2. * @brief 临界区访问类,主要封装windows临界区的访问,该类主要在栈中使用,利用局部变量的构造和析构函数出入临界区
  3. * @author 徐敏荣
  4. * @date 2012-06-14
  5. *
  6. * @par 修订历史
  7. * @version v0.5 \n
  8. * @author 徐敏荣
  9. * @date 2012-06-14
  10. * @li 初始版本
  11. *
  12. */
  13. class HiCritical
  14. {
  15. public:
  16.  
  17. /**
  18. * @brief 构造函数
  19. */
  20. HiCritical()
  21. {
  22. ::InitializeCriticalSection(&cs);
  23. }
  24.  
  25. /**
  26. * @brief 析构函数
  27. */
  28. ~HiCritical()
  29. {
  30. ::DeleteCriticalSection(&cs);
  31. }
  32.  
  33. /**
  34. * @brief 进入临界区
  35. */
  36. void Enter()
  37. {
  38. ::EnterCriticalSection(&cs);
  39. }
  40.  
  41. /**
  42. * @brief 离开临界区
  43. */
  44. void Leave()
  45. {
  46. ::LeaveCriticalSection(&cs);
  47. }
  48.  
  49. CRITICAL_SECTION* GetSection()
  50. {
  51. return &cs;
  52. }
  53. private:
  54.  
  55. /**
  56. * @brief 临界区对象
  57. */
  58. CRITICAL_SECTION cs; /**< 临界区对象 */
  59. };
 
 
另外还提供一个临界区管理类(HiCriticalMng),在构造该类时,进入临界区,析构该类时离开临界区。如果构造函数中传入的是NULL,则不进行任何互斥处理。

  1. /**
  2. * @brief 临界区访问管理类,利用构造函数进入临界区,利用西沟函数离开临界区
  3. * 如果向构造函数提供NULL参数,则不使用临界区。
  4. *
  5. */
  6. class HiCriticalMng
  7. {
  8. public:
  9. HiCriticalMng(HiCritical& crl): cl(&crl)
  10. {
  11. cl->Enter();
  12. }
  13.  
  14. HiCriticalMng(HiCritical* crl): cl(crl)
  15. {
  16. if (cl)
  17. {
  18. cl->Enter();
  19. }
  20. }
  21.  
  22. ~HiCriticalMng()
  23. {
  24. if (cl)
  25. {
  26. cl->Leave();
  27. }
  28. }
  29.  
  30. private:
  31. HiCritical* cl;
  32. };
 
(三) HiDBImpl的接口
作为数据库访问类,HiDBImpl实现HiDB需要的所有接口,所以HiDBImpl与HiDB接口类似,但是HiDBImpl接口接收的参数为完整的SQL语句(因为带可变参数的SQL语句已经被HiDB处理了)。
HiDBImpl不但要支持MySQL,以后还要支持其他数据库,所以不能有LibMySQL相关的东西,HiDBImpl应该是一个基类,可以被派生,例如派生出支持LibMySQL的子类。
HiDBImpl要线程安全的,所以要包含互斥锁HiCritical,又要可以非线程安全(HiCriticalMng支持NULL参数),所以HiCritical需要时这指针,这样,HiDBImpl的接口就出来了。
HiDBImpl接口如下:

  1. #pragma once
  2. /**
  3. * @defgroup 数据库操作实现类接口类
  4. * @brief 数据库操作实现类接口类,声明数据库操作实现类的接口。
  5. * @author 徐敏荣
  6. * @date 2012-06-14
  7. *
  8. * @par 修订历史
  9. * @version v0.5 \n
  10. * @author 徐敏荣
  11. * @date 2012-06-14
  12. * @li 初始版本
  13. * @{
  14. */
  15.  
  16. #include "DB/HiDB.h"
  17. class HiCritical;
  18. /**
  19. * @brief 数据库操作实现类接口类,声明数据库操作实现类的接口
  20. *
  21. */
  22. class HiDBImpl
  23. {
  24. public:
  25.  
  26. /**
  27. * @brief 构造函数
  28. * @param[in] isUsingLock 是否需要使用互斥锁
  29. */
  30. HiDBImpl(bool isUsingLock);
  31.  
  32. /**
  33. * @brief 析构函数
  34. */
  35. virtual ~HiDBImpl();
  36.  
  37. public:
  38.  
  39. /**
  40. * @brief 打开数据库连接
  41. * @param[in] conn 数据库连接字符串
  42. * @retval true:成功,false;失败
  43. */
  44. virtual bool Open(const char* conn) = ;
  45.  
  46. /**
  47. * @brief 关闭据库连接
  48. */
  49. virtual void Close(void) = ;
  50.  
  51. public:
  52.  
  53. /**
  54. * @brief 执行SQL语句,并不返回结果
  55. * @param[in] conn SQL语句
  56. * @retval true:成功,false;失败
  57. */
  58. virtual bool ExecuteNoQuery(const char* sql) = ;
  59.  
  60. public:
  61.  
  62. /**
  63. * @brief 执行SQL语句,返回一个结果
  64. * @param[in] sql SQL语句
  65. * @param[out] value 取得的结果
  66. * @retval true:成功,false;失败
  67. */
  68. virtual std::string ExecuteScalar(const char* sql) = ;
  69. public:
  70.  
  71. /**
  72. * @brief 执行SQL语句,返回一个结果集合
  73. * @param[in] sql SQL语句
  74. * @param[out] table 取得的结果集合
  75. * @retval true:成功,false;失败
  76. */
  77. virtual std::shared_ptr<HiDBTable> ExecuteQuery(const char* sql) = ;
  78.  
  79. public:
  80.  
  81. /**
  82. * @brief 事物处理
  83. * @retval true:成功,false;失败
  84. */
  85. virtual void OnTransaction(const std::function<void()>& fun) = ;
  86.  
  87. protected:
  88.  
  89. /**
  90. * @brief 临界区对象,为空表示不需要考虑资源并发访问
  91. */
  92. HiCritical* m_pCritical;
  93. };
  94.  
  95. /**//** @}*/ // 数据库操作实现类接口类
 
(四)HiDB的实现:
由HiDB负责实现可变参数转换为完整SQL语句,HiDBImpl负责实现所有数据库访问逻辑,并要为以后添加其他数据库支持这些需求可以推到出HiDB的实现代码:
  1. #include <stdarg.h>
  2. #include "DB/HiDB.h"
  3. #include "HiDBMySQL.h"
  4.  
  5. using namespace std;
  6.  
  7. #if !defined(HISDB_ON_VARLIST)
  8. #define HISDB_ON_VARLIST(x, y) \
  9. char chArr[] = {};\
  10. char* pchar = &chArr[];\
  11. va_list pArgList;\
  12. va_start(pArgList, y);\
  13. ::_vsnprintf(chArr, , x, pArgList); \
  14. va_end(pArgList) ;
  15. #endif
  16.  
  17. static bool IsImplOK(HiDBImpl* db)
  18. {
  19. if (!db)
  20. {
  21. return false;
  22. }
  23. /*
  24. if (!db->IsOpen())
  25. {
  26. return false;
  27. }*/
  28. return true;
  29. }
  30.  
  31. // 构造函数
  32. HiDB::HiDB(HiDBType type, bool isUsingLock):m_Impl(NULL)
  33. {
  34. if (type == HiDBType_MySQL)
  35. {
  36. this->m_Impl = new HiDBMySQL(isUsingLock);
  37. }
  38. }
  39.  
  40. // 析构函数
  41. HiDB::~HiDB()
  42. {
  43. if (this->m_Impl)
  44. {
  45. delete this->m_Impl;
  46. this->m_Impl = NULL;
  47. }
  48. }
  49.  
  50. // 打开数据库连接
  51. bool HiDB::Open(const char* conn)
  52. {
  53. if (!this->m_Impl)
  54. {
  55. return false;
  56. }
  57.  
  58. return this->m_Impl->Open(conn);
  59. }
  60.  
  61. bool HiDB::IsOpen()
  62. {
  63. if (!this->m_Impl)
  64. {
  65. return false;
  66. }
  67.  
  68. return true;//this->m_Impl->IsOpen();
  69. }
  70.  
  71. void HiDB::Close(void)
  72. {
  73. if (!IsImplOK(this->m_Impl))
  74. {
  75. return;
  76. }
  77.  
  78. return this->m_Impl->Close();
  79. }
  80.  
  81. bool HiDB::ExecuteNoQuery(const char* sql, ...)
  82. {
  83. if (!IsImplOK(this->m_Impl))
  84. {
  85. return false;
  86. }
  87.  
  88. HISDB_ON_VARLIST(sql, sql);
  89.  
  90. return this->m_Impl->ExecuteNoQuery(chArr);
  91. }
  92.  
  93. string HiDB::ExecuteScalar(const char* sql, ...)
  94. {
  95. if (!IsImplOK(this->m_Impl))
  96. {
  97. return "";
  98. }
  99.  
  100. HISDB_ON_VARLIST(sql, sql);
  101.  
  102. return this->m_Impl->ExecuteScalar(chArr);
  103. }
  104.  
  105. std::shared_ptr<HiDBTable> HiDB::ExecuteQuery(const char* sql, ...)
  106. {
  107. if (!IsImplOK(this->m_Impl))
  108. {
  109. return NULL;
  110. }
  111. HISDB_ON_VARLIST(sql, sql);
  112.  
  113. return this->m_Impl->ExecuteQuery(chArr);
  114. }
  115.  
  116. void HiDB::OnTransaction(const std::function<void()>& fun)
  117. {
  118. if (!IsImplOK(this->m_Impl))
  119. {
  120. HiDBHelperOnError("HiDB::OnTransaction",
  121. "HiDB is not impl", "", );
  122. }
  123. return this->m_Impl->OnTransaction(fun);
  124. }
 
四 后记
至此,HiDB所有主要的接口和主要的实现就介绍的差不多,其他更多的实现,可以参照源码自己实现。类图将在本文后面提供。
后续的工作包括对HiDB进行测试,我期望能进行自动化测试,类似于Junit。但是我并不知道C++有哪些自动化测试工具,没办法,只有自己写C++自动化测试工具来测试HiDB了,后面的文章我打算介绍一下我写的一个C++自动化测试工具。
HiDB类图:

 
源代码:http://download.csdn.net/detail/xumingxsh/7778417
 
本文为我从自己的csdn博客转过来的文章,应该算是原创吧

学习实践:使用模式,原则实现一个C++数据库访问类的更多相关文章

  1. 一个通用数据库访问类(C#,SqlClient)

    本文转自:http://www.7139.com/jsxy/cxsj/c/200607/114291.html使用ADO.NET时,每次数据库操作都要设置connection属性.建立connecti ...

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

    一.概述 在我參与的多个项目中.大家使用libMySQL操作MySQL数据库,并且是源代码级复用,在多个项目中同样或相似的源代码.这种复用方式给开发带来了不便. libMySQL的使用比較麻烦.非常e ...

  3. 一个C#的XML数据库访问类

    原文地址:http://hankjin.blog.163.com/blog/static/33731937200942915452244/ 程序中不可避免的要用到配置文件或数据,对于数据量比较小的程序 ...

  4. [node.js学习]为node.js写的一个操作mysql的类

    不支持一个对象在不同异步中使用,模仿的php框架 speedphp中的model模块 GaryMysql.js var mysql = require('mysql'); var pool = nul ...

  5. C++实现一个单例模板类

    单例模式在项目开发中使用得比较多,一个单例的模板类显得很有必要,避免每次都要重复定义一个单例类型 //非多线程模式下的一个单例模板类的实现 // template_singleton.h #inclu ...

  6. 学习实践:使用模式,原则实现一个C++自动化测试程序

    个人编程中比较喜欢重构,重构能够提高自己的代码质量,使代码阅读起来也更清晰.但是重构有一个问题,就是如何保证重构后带代码实现的功能与重构前的一致,如果每次重构完成后,对此不闻不问,则会有极大的风险,如 ...

  7. DRY原则的一个简单实践

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://dzone.com/articles/dry-dont-repeat-yourse ...

  8. 编码最佳实践——Liskov替换原则

    Liskov替换原则(Liskov Substitution Principle)是一组用于创建继承层次结构的指导原则.按照Liskov替换原则创建的继承层次结构中,客户端代码能够放心的使用它的任意类 ...

  9. 前端学习实践笔记--JavaScript深入【1】

    这一年中零零散散看过几本javascript的书,回过头看之前写过的javascript学习笔记,未免有点汗颜,突出“肤浅”二字,然越深入越觉得javascript的博大精深,有种只缘身在此山中的感觉 ...

随机推荐

  1. linux下通过进程名查看其占用端口

    linux下通过进程名查看其占用端口: 1.先查看进程pid ps -ef | grep 进程名 2.通过pid查看占用端口 netstat -nap | grep 进程pid 例:通过nginx进程 ...

  2. ImportError: DLL load failed: 找不到指定的模块

    如果遇到错误:ImportError: DLL load failed: 找不到指定的模块出现错误原因:安装包的来源问题,也可以理解为包版本兼容问题,有的包使用官方出版,有的包使用whl文件安装 解决 ...

  3. mysql delete别名

    有一个表的数据比较大,然后需要进行关联删除,删除的时候发现如下SQL报错:ELETE FROM test.test1 a WHERE EXISTS (SELECT 1 FROM test.test2 ...

  4. java TimeUnit 的使用

    主要作用 时间颗粒度转换 延时 1.时间颗粒度转换 public long toMillis(long d) //转化成毫秒 public long toSeconds(long d) //转化成秒 ...

  5. 【Elasticsearch】Docker 安装 Elasticsearch 2.4.4 版本(高版本方式不同)

    1. 下载  elasticsearch docker pull docker.elastic.co/elasticsearch/elasticsearch:6.4.3 2.启动 elasticsea ...

  6. 【Spring Boot】 Spring Boot 2.x 版本 CacheManager 配置方式

    Spring Boot 1.X RedisCacheManager 配置方式 @Bean public CacheManager cacheManager(RedisTemplate redisTem ...

  7. Leetcode题目160.相交链表(简单)

    题目描述 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = ...

  8. ci框架总结(一)

    在进行数据库操作前一定要先初始化数据库类:$this->load->database(); 在model类中: class Myiapp_model extends CI_Model{ p ...

  9. 带你体验Android自定义圆形刻度罗盘 仪表盘 实现指针动态改变

    带你体验Android自定义圆形刻度罗盘 仪表盘 实现指针动态改变 转 https://blog.csdn.net/qq_30993595/article/details/78915115   近期有 ...

  10. Android云端APP

    使用HbuilderX打包APP 首先创建一个 5+APP+使用MUI项目 <!DOCTYPE html> <html> <head> <meta chars ...