Learning practice: Using patterns and principles to implement a C + + database access class

Source: Internet
Author: User
Tags doxygen mutex throw exception

First, outlined in my participation in a number of projects, we use LIBMYSQL operation MySQL database, and is the source code is reused, in many projects there are many sets of identical or similar source code, such a reuse way to the development brought the same, and the use of libmysql more trouble, to deal with a lot of detail, It's easy to make mistakes. I'm going to write a dynamic link library that encapsulates the operation of the Libmysql and replaces the source-level reuse with binary multiplexing; To provide a thread-safe interface, the user does not need to have a relationship locking such a detail problem, reducing the chance of errors and deadlocks, and of course allowing the user to choose whether to thread-safe access to the database To simplify the process of accessing the database, the simpler the interface, the better.   I started writing this library since 2011, and I named it hidb. Hidb from the 2011 to the present, through a simple to complex and complex to a simple process, the middle also contains their own understanding of the process of change. Many of the considerations look fine, such as the annotation style of the code (choosing our usual annotation style or the Doxygen annotation style, and the annotation style in the Code encyclopedia. Whether it needs to be borrowed); The Code naming conventions (Hungarian nomenclature or java,c# nomenclature, and many naming conventions for open source projects), whether the policy classes can be applied to the library in the new C + + design, and the interfaces and implementations of effective C + + are separated. Some of the things that I've thought about over the years include the choice of annotation styles (our usual annotations, the Doxygen annotation style, and the annotation style in the Code encyclopedia), whether error is used to report an error or to use an exception to report an error when an error occurs? C + + Design new thinking about the policy classes,effective interface and implementation analysis in C + + (IMPL) Two, interface (i) interface overview first determine the interface of HIDB. The library is displayed externally as a HIDB class that should contain interfaces that include: 1: User-selected thread-safe or non-thread-safe 2: Open database connection 3: Close database connection 4: Perform non-query operations such as Insert,update Interface (executenoquery) 5: Get the first column of the query results interface (Executescaler) 6: Get all records of a query (multi-row multi-column) interface (ExecuteQuery) 7: Interface for performing transactions (ontransaction)   All interfaces are thrown when an exception is generated , it is necessary to make an encapsulation of the exception, it is best to identify the location of the exception, the exception number, the description of the exception, the SQL statement that caused the exception. The exception I designed is as follows: 
/** @brief Database Operation exception */class Hi_db_export hidbexception{public:hidbexception ();p ublic:std::string tosrting ();p ublic: Std::string M_sql; /**< SQL statement for this operation */std::string M_descript; /**< anomaly Description */std::string m_position; /**< abnormal position */long M_errorid; /**< exception number */hidbtype M_dbtyp; /**< database type */};

For the convenience of throwing exceptions, I have provided a macro (this macro is only used by the HIDB library)
/** @brief EXCEPTION Statement macro */#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;

To provide this macro, in addition to simplifying user input, there is an idea that if the user does not want to use an exception, you can modify the macro, such as returning false. Familiar with the Friends of ADO should be able to see these interfaces are borrowed from the ADO. (b) Specific interfaces <1> constructors originally in "C + + Design new thinking", there is a policy classes, suitable for the needs of users to provide a secure or non-secure interface, but the policy classes suitable for the template, not suitable for non-template, So there is no use here, just add a Boolean parameter Isusinglock in the constructor, if true provides a thread-safe interface, otherwise the interface is non-thread-safe. HIDB intends to support other databases in the future in addition to MySQL, so in the constructor, in addition to Isusinglock, there is an interface to select the database type. To write the database type as an enumeration, the enumeration is:
/** @brief Database Type */enum hidbtype{hidbtype_invail,/**< Invalid type */hidbtype_mysql,/**< MySQL */};  The declaration of the constructor is clear:/*** @brief constructor * @param [in] type database type * @param [in] isusinglock whether you need to use a mutex */hidb (hidbtype type = hidbtype_my SQL, bool Isusinglock = false); <2> Open database connection Open database connection simple:/*** @brief Open Database connection * @param [in] conn database connection String * @retval true: Success, FALSE, Failure * @par instance: * @code * HiD B db;* if (db. Open ("HOST=127.0.0.1;PORT=3306;DBNAME=TEST;USER=ROOT;PWD=ROOT;CHARSET=GBK;")) * {*//Open Success *}* else* {*//Open Failed *}* @endcode */bool Open (const char* conn) throw (hidbexception);

The conn parameter of the interface is a string, so that the string is extensible and can be handled differently for different databases (which is not a good feeling, but it provides the stability of the interface). Different databases need to meet a specific format, in MySQL, to be used similar to "HOST=127.0.0.1;PORT=3306;DBNAME=TEST;USER=ROOT;PWD=ROOT;CHARSET=GBK;" The format. <3> Close Database connection
/*** @brief Close according to the library connection */void close (void);

<4> IsOpen Interface This interface is extended, since there is open,close, providing a isopen seems to be, let the user understand the current open state.
/*** @brief The database connection is open */bool IsOpen ();

<5> executes the interface of a non-query statement interface execution SQL statement, should be able to receive mutable parameters, in addition to mutable parameters, there should be a string parameter containing the SQL statement, so the interface is defined as follows:
/*** @brief executes the SQL statement and does not return the result * @param [in] conn SQL statement * @retval true: Success, FALSE, Failure * @par instance: * @code * HIDB db;* if (db). Executenoquery ("UPDATE table SET paramer1= '%s ' * and paramer2= '%s ' OR paramer3=%d", "Test1", "Test2", 3)) * {*//execution succeeded *}* E lse* {*//execution failed *}* @endcode */bool executenoquery (const char* SQL, ...) throw (hidbexception);

<6> get the first row of query results The interface's parameters are consistent with the interface that executes non-query statements, but the return value should be a string, and an empty string should be returned if execution fails. Throws an Hidbexception exception when an exception is triggered.
/*** @brief Execute SQL statement, return a result * @param [in] SQL SQL statement * @retval obtained data, if empty, fails */std::string executescalar (const char* SQL, ...) throw (hidbexception);

<7> the interface that gets all the records for a single query (multiple rows and columns) The parameters of the interface are consistent with the interface that executes non-query statements. The returned result should be a dataset with multiple rows and columns, and there is a DataTable in ADO, where we can store multiple rows with vectors in the STL, and map stores multiple columns of data per row. More than one dataset needs to be defined:
#ifndef hidbtable typedef std::map<std::string, std::string> Hidbmap; /** @brief Query Results */typedef std::vector

Because hidbtable contains multiple maps, it is best to avoid copying and use STL's shared_ptr to avoid multiple copies:
/*** @brief executes the SQL statement, returns a result set * @param [in] SQL SQL statement * @retval a smart pointer to store query records */std::shared_ptr

<8> interface Execution Transaction interface is a command-mode interface, and the parameter should be a function object. The object is a function object with no return value for the parameter. The function object is provided in the STL. (In the original version, the function object was implemented by itself)
/*** @brief perform processing in a transaction * @param [in] the fun handler function */void ontransaction (const std::function<void () >& fun) throw ( Hidbexception);

(iii) Use cases of the interface
Hidb m_db = new Hidb (Hidbtype_mysql, true); Try{bool ret = M_db->open ("host=127.0.0.1;port=3306;dbname=test;user= ROOT;PWD=ROOT;CHARSET=GBK; "); M_db->executenoquery ("drop table if exists table1;"); String val = M_db->executescalar ("Select Column4 from table1 WHERE column1= '%s ' and column3=%d", &val, "Hitest", 59) ;shared_ptr

(iv) Other actually, I previously provided a more complex interface than now, first I imitate ADO, the SQL parameters are encapsulated, encapsulated the SQL parameter name, type, whether it is empty, value and other information, the obtained data are similarly encapsulated. , the Create and delete functions are also provided for the SQL parameters. Having a look at my interface at the same time said that my interface is too complex, layering is not clear. I took his advice and changed the interface to the current interface. In addition, the execution of the transaction interface, I was the first to create a function object, which also increased the complexity, later using the STL function object, supplemented by lambda expression, it is much simpler to use. (v) Complete interface: <1>hidbcommon.h provides related enumerations and structs to which the interface is applied
 #pragma once/*** @defgroup database module * @{*/#include "HiDBExport.h" #include <string> #include <vector># Include <map> #include <sstream>/** @brief database type */enum hidbtype{hidbtype_invail,/**< Invalid type */hidbtype_ MySQL,/**< mysql */}; #ifndef hidbtable typedef std::map<std::string, std::string> Hidbmap; /** @brief Query Results */typedef std::vector

<2> HiDB.h Main interface:
#pragma once/*** @defgroup database module * @{*/#include <memory> #include <functional> #include "HiDBCommon.h" Class H Idbimpl; #pragma warning (disable:4290)/*** @brief database Operation class, encapsulates the common operation of the database, this class uses the policy mode implementation * @author Xu Min Rong * @date 2012-06-14** @par Revision history * @vers Ion v0.5 \n* @author Xu min Glory * @date 2012-06-14* @li Initial release * @version v0.6 \n* @author Xu Min sakae * @date 2014-08-04* @li Simplified program **/class HI _db_export hidb{public:/*** @brief constructor * @param [in] type database type * @param [in] isusinglock need to use mutex */hidb (hidbtype type = Hi Dbtype_mysql, bool Isusinglock = false); /*** @brief destructor */~hidb (); Public:/*** @brief Open Database connection * @param [in] conn database connection String * @retval true: Success, FALSE, Failure * @par instance: * @code * HIDB db;* if (db). Open ("HOST=127.0.0.1;PORT=3306;DBNAME=TEST;USER=ROOT;PWD=ROOT;CHARSET=GBK;")) * {*//Open Success *}* else* {*//Open Failed *}* @endcode */bool Open (const char* conn) throw (hidbexception); /*** @brief Close according to the library connection */void close (void); /*** @brief The database connection is open */bool IsOpen (); Public:/*** @brief Execute SQL statement and do not return result * @param [in] conn SQL statement * @retval tRue: Success, False, Failure * @par instance: * @code * HIDB db;* if (db). Executenoquery ("UPDATE table SET paramer1= '%s ' * and paramer2= '%s ' OR paramer3=%d", "Test1", "Test2", 3)) * {*//execution succeeded *}* E lse* {*//execution failed *}* @endcode */bool executenoquery (const char* SQL, ...) throw (hidbexception); Public:/*** @brief Execute SQL statement, return a result * @param [in] SQL SQL statement * @retval obtained data, if empty, fails */std::string executescalar (const char* S QL, ...) throw (hidbexception); Public:/*** @brief Execute SQL statement, return a result set * @param [in] SQL SQL statement * @retval A smart pointer that stores query records */std::shared_ptr

The implementation of the three implementations adopts the principle of the realization and interface analysis learned from the effective C + +, and the logic of accessing the database is realized by using Hidbimpl in HIDB. (i) Processing of variable parameters of course, in hidb it is necessary to solve the problem of assembling a complete SQL statement based on SQL parameters and mutable parameters. The issue uses a macro to implement:
#if!defined (hisdb_on_varlist) #define Hisdb_on_varlist (x, y) char charr[2048] = {0};char* Pchar = &charr[0];va_list p Arglist;va_start (parglist, y);:: _vsnprintf (Charr, 2047, X, parglist); Va_end (parglist); #endif

(ii) The realization of the mutual exclusion lock according to the critical section, the realization of a mutual exclusion lock, the mutex interface is as follows: 1: constructor: Implementation of critical section initialization 2: destructor: Realization of critical section deletion 3: Enter the critical section 4: Exit the critical section of the implementation function as follows:
/*** @brief critical section Access class, mainly encapsulates access to the Windows critical section, which is used primarily in the stack, using the construction and destructor of local variables to access critical areas * @author Xu Min Rong * @date 2012-06-14** @par Revision history * @version v0.5 \n* @author Xu Min Rong * @date 2012-06-14* @li initial release **/class hicritical{public:/*** @brief Constructor */hicritical () {:: INITIALIZECR Iticalsection (&CS);} /*** @brief destructor */~hicritical () {::D eletecriticalsection (&cs),}/*** @brief Enter the critical section */void enter () {:: EnterCriticalSection (&CS);} /*** @brief leave the critical section */void Leave () {:: LeaveCriticalSection (&CS);} critical_section* GetSection () {return &cs;} Private:/*** @brief critical section object */critical_section CS; /**< critical Section Object */};

In addition, a critical section management class (HICRITICALMNG) is provided to enter the critical section when the class is constructed, and to leave the critical section when the class is refactored. If NULL is passed in the constructor, no mutex is processed.
/*** @brief critical section Access management class, using the constructor to enter the critical section, using the function of the XI Xi to leave the critical section * If a null argument is supplied to the constructor, no critical section is used. **/class hicriticalmng{public:hicriticalmng (hicritical& CRL): Cl (&CRL) {cl->enter ();} HICRITICALMNG (hicritical* CRL): Cl (CRL) {if (CL) {cl->enter ();}} ~hicriticalmng () {if (CL) {cl->leave ();}} private : hicritical* cl;};

(iii) HIDBIMPL interface as a database access class, Hidbimpl implements all the interfaces required by HIDB, so Hidbimpl is similar to HIDB interface, However, the Hidbimpl interface receives a full SQL statement (because SQL statements with mutable arguments have been hidb processed). Hidbimpl not only to support MySQL, but also to support other databases later, so there is no libmysql related things, Hidbimpl should be a base class, can be derived, for example, derived from supporting Libmysql subclasses. Hidbimpl to be thread-safe, so to include the mutex hicritical, but also non-thread-safe (HICRITICALMNG support null parameters), so hicritical need this pointer, so that the Hidbimpl interface is out. The Hidbimpl interface is as follows:
#pragma once/*** @defgroup database operation implement class interface class * @brief database operation implements class interface class, declares the interface of database operation implementation class. * @author Xu min @date 2012-06-14** @par Revision history * @version v0.5 \n* @author Xu Min sakae * @date 2012-06-14* @li Initial version * @{*/#include "db/ HiDB.h "class hicritical;/*** @brief database operation implements the class interface class, declares that the database operation implements the interface of the class **/class Hidbimpl{public:/*** @brief constructor * @param [in] Isusinglock need to use mutex */hidbimpl (bool isusinglock); /*** @brief destructor */virtual ~hidbimpl (); Public:/*** @brief Open Database connection * @param [in] conn database connection String * @retval true: success, false; failure */virtual BOOL Open (CONST char* conn) = 0; /*** @brief closed According to the library connection */virtual void close (void) = 0; Public:/*** @brief Execute SQL statement and do not return result * @param [in] conn SQL statement * @retval true: Success, false; */virtual bool Executenoquery (const CH ar* sql) = 0; Public:/*** @brief Execute SQL statement, return a result * @param [in] SQL SQL statement * @param [out] value obtained result * @retval true: Success, FALSE, Failure */virtual std:: String executescalar (const char* SQL) = 0;public:/*** @brief Execute SQL statement, return a result set * @param [in] SQL SQL statement * @param [out] table obtained Result set * @retval true: Success, false, failure */virtual Std::shAred_ptr

(iv) Implementation of the HIDB: the HIDB is responsible for implementing the variable parameter conversion to the full SQL statement, Hidbimpl is responsible for implementing all the database access logic, and to add additional databases to support these requirements can be pushed out HIDB implementation code:
#include <stdarg.h> #include "db/hidb.h" #include "HiDBMySQL.h" using namespace std; #if!defined (hisdb_on_varlist) #define Hisdb_on_varlist (x, y) char charr[2048] = {0};char* Pchar = &charr[0];va_list p Arglist;va_start (parglist, y);:: _vsnprintf (Charr, 2047, X, parglist); Va_end (parglist); #endif static bool Isimplok (hidbimpl* db) {if (!db) {return false;} /*if (!db->isopen ()) {return false;} */return true;} Constructor Hidb::hidb (hidbtype type, bool Isusinglock): M_impl (NULL) {if (type = = Hidbtype_mysql) {This->m_impl = new Hidbmysql (Isusinglock);}} destructor Hidb::~hidb () {if (this->m_impl) {Delete This->m_impl;this->m_impl = NULL;}}//Open database connection bool Hidb::open ( CONST Char* conn) {if (!this->m_impl) {return false;} return This->m_impl->open (conn);} bool Hidb::isopen () {if ( !this->m_impl) {return false;} return True;//this->m_impl->isopen ();} void Hidb::close (void) {if (! Isimplok (This->m_impl)) {return;} return This->m_impl->close ();} BOOL Hidb::executenoquery (cOnst char* sql, ...) {if (! Isimplok (This->m_impl)) {return false;} hisdb_on_varlist (SQL, SQL); Return This->m_impl->executenoquery (Charr);} String hidb::executescalar (const char* SQL, ...) {if (! Isimplok (This->m_impl)) {return "";} hisdb_on_varlist (SQL, SQL); Return This->m_impl->executescalar (Charr);} Std::shared_ptr

Four postscript to this point, hidb all the main interface and the main implementation of the introduction of almost, other more implementations, you can refer to the source code to achieve their own. Class diagrams are provided later in this article. The following work involves testing the HIDB and I expect to be able to perform automated tests similar to JUnit. But I do not know what the C + + has automated testing tools, no way, only to write their own C + + automated testing tools to test HIDB, the following article I would like to introduce a C + + automated test tools I wrote. Hidb class Diagram: Source code: http://download.csdn.net/detail/xumingxsh/7778417 I use "upstream vessel" account to publish not to the homepage, use this account to publish it. The program on its own to continue to write the revised for several years, I do not believe that no one to see, no one feel useful.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.