基于模板实现对MYSQL query封装

来源:互联网 发布:罗技 m705 mac 编辑:程序博客网 时间:2024/05/17 22:04

前言

MYSQL C API的常见操作可以分为两类:querystatementquery着重的是返回结果集;statement则着重执行结果和affected row。当然如果想忽略执行结果和affected row,也可以用query来执行statement的任务。最常见的是query执行SELECT语句而statement执行INSERT/UPDATE/DELETE和存储过程。MYSQLAPImysql_real_querymysql_stmt_init/mysql_stmt_prepare/mysql_stmt_execute。详细资料请参考http://dev.mysql.com/doc/refman.

设计思想

query而言,标准步骤为:

1) 构造SQL;

2) 执行SQL;

3) 解析结果集, MYSQLMYSQL_ROW,实际为char**;

4) 异常处理。

可变的部分为SQL和结果集解析,因为SELECT可以SELECT多个COLUMN;但骨架操作高度一致。正好符合模板的设计思想。于是定义了一个模板函数。

//template function to run a query sql and save the results into a container

template <class H, class T>

bool query(H& rh, const string& query_sql, vector<T>& array)

此模板有两个模板参数:

1) 一行结果集的处理类;

2) 一行结果集的容器类。

class ExampleContainer:

{

public:

int col1;

string col2;

unsigned char* col3;

int buffer_len;

};

class ExampleRowHandler

{

public

bool handle_row(MYSQL_ROW row, unsigned long* lengths, ExampleContainer& ec);

};

实际使用时的代码为:

ostringstream oss;

oss << “SELECT col1, col2, col3 FROM example”;

ExampleRowHandler ech;

vector< ExampleContainer> ecs;

if(!query(ech, oss.str(), ecs))

{                             

                throw DBGoneException("failed to execute query:" + oss.str());

}

而一行的处理就是比较个案的代码。

bool ExampleRowHandler:: handle_row(MYSQL_ROW row, unsigned long* lengths, ExampleContainer& ec)

{

      try

     {

          register int i = 0;

          if(!get_from_string(ec.col1, row[i], lengths[i]))

         {                                                                   

                throw STLException("failed to get col1");

         }

          i++;

           if(!get_from_string(ec.col2, row[i], lengths[i]))

         {                                                                   

                throw STLException("failed to get col2");

         }

          i++;

         ec.col3 = new unsigned char[lengths[i]];

         ec.buffer_len = lengths[i];

          if(!get_from_string(ec.col3, row[i], lengths[i]))

         {                                                                   

                throw STLException("failed to get col3");

         }

     }

     catch(STLException& dbe)

     {

     }  

}

 

对于只SELECT一个COLUMN的情况,完全可以定义一个模板类做处理。

template <class T>           

class RowHandlerOneC

{

public:                                  

                bool handle_row(MYSQL_ROW row, unsigned long* lengths, T& t)

                {

                                if(!get_from_string(t, row[0], lengths[0]))

                                {

                                    cerr << "failed to get ts from string." << endl;

                                    return false;   

                                }             

                              return true;

                }                             

};

注意到这里对char**的处理用到了模板函数get_from_string。此模板函数实际是用标准库函数的流操作替代了Csscanf

//tools to get msyql results; typedef char** MYSQL_ROW

//use the string stream to convert those string into the specific type

template <class T>

bool get_from_string(T& t, const char* s, int length)

{

    istringstream iss(string(s, length));

    iss >> t;

    return !iss.fail();

}

对于一类特殊的类型,unsigned char,此函数出现截断的问题(’/0’)。所以必须提供一个完全特化版本。

//the full specicailized version for string converter

//unsigned char array is special because possible '/0' exist.

//if you handle it as a string, the array will be broken.

template <>

bool get_from_string<unsigned char*>(unsigned char*& t, const char* s, int length)

{

    for(int i = 0; i < length; i ++)

    {

        t[i] = (unsigned char) s[i];

    }

    return true;

}

现在列出完整的代码。

//template function to run a query sql and save the results into a container

template <class H, class T>

bool query(H& rh, const string& query_sql, vector<T>& array)

{

                try

                {

                                if(m_pMySQL == NULL)

                                {

                                               throw STLException("mysql client connection is not initialized.");

                                }

             if(mysql_real_query(m_pMySQL, query_sql.c_str(), query_sql.length()))

                {

                                    throw STLException(query_sql + " failed. Error: " + mysql_error(m_pMySQL));                          

                  }                               

                    m_pMySQLRes = mysql_store_result(m_pMySQL);

                    if(!m_pMySQLRes)

                    {

                                if(mysql_field_count(m_pMySQL) == 0)

                                {

                                                cerr << "NULL result set, row affected: " << mysql_affected_rows(m_pMySQL) << endl;

                                }

                                else

                                {

                                    throw STLException("failed to get the result.");

                                }

                    }

                         else

                    {

                                int num_rows = mysql_num_rows(m_pMySQLRes);                                                      

                                if(num_rows == 0)

                                {

                                                cerr << "got result set, rows affected: " << mysql_affected_rows(m_pMySQL) << endl;

                                }

                                else

                                {                                                             

                                                ubit32 num_fields = mysql_num_fields(m_pMySQLRes);

                                                                   for(int i = 0; i < num_rows; i ++)

                                                                   {                                                                                         

                                                                               MYSQL_ROW row = mysql_fetch_row(m_pMySQLRes);

                                                                               unsigned long *lengths = mysql_fetch_lengths(m_pMySQLRes);                           

                                                                               if(row && lengths)

                                                                               {                                                             

                                                                                               Holder<T> h;

                                                                                               T t = h.get_inst();

                                                                                               if(!rh.handle_row(row, lengths, t))

                                                                                               {                                                                                                             

                                                                                                               throw STLException("failed to handle rows.");

                                                                                               }                                                    

                                                                                               else

                                                                                               {

                                                                                                               array.push_back(t);

                                                                                               }

                                                                               }

                                                       }

                                                   }                                                              

                    }

                    catch(STLException& dbe)

                    {                         

                                if(!m_pMySQLRes)

                                {

                                                 mysql_free_result(m_pMySQLRes);

                }

                return false;

    }

                       

    return true;

                }

 

 

 

 

必须注意到一个细节:模板类Holder。如果模板函数query传入是指针类型,如果直接直接定义T t就会出现指针未初始化的问题。你也可以对指针类型做特化处理,但大部分代码就会相同,完全没有必要。定义一个模板类就要解决这个问题:对这个模板类做特化,代码不会重复。

   template<class T>

                class Holder

                {

                public:

                                T get_inst()

                                {

                                               return t;

                                }

                private:

                                T t;

                };         

 template<class T>

                class Holder<T*>

                {

                public:

                                T* get_inst()

                                {

                                               return new T();

                                }             

                };

后记

statement完全也可以这样封装,把常用的动作都抽象出来定义诸如INSERT, UPDATE, DELETE, STORED PROCEDURE的执行和结果处理。

原创粉丝点击