基于模板实现对MYSQL query封装
来源:互联网 发布:罗技 m705 mac 编辑:程序博客网 时间:2024/05/17 22:04
前言
MYSQL C API的常见操作可以分为两类:query和statement。query着重的是返回结果集;statement则着重执行结果和affected row。当然如果想忽略执行结果和affected row,也可以用query来执行statement的任务。最常见的是query执行SELECT语句而statement执行INSERT/UPDATE/DELETE和存储过程。MYSQL的API为mysql_real_query,mysql_stmt_init/mysql_stmt_prepare/mysql_stmt_execute。详细资料请参考http://dev.mysql.com/doc/refman.
设计思想
对query而言,标准步骤为:
1) 构造SQL;
2) 执行SQL;
3) 解析结果集, 对MYSQL是MYSQL_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。此模板函数实际是用标准库函数的流操作替代了C的sscanf。
//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的执行和结果处理。
- 基于模板实现对MYSQL query封装
- 基于query语句解析mysql工作原理
- 基于原生JavaScript封装对象模板
- 对mysql的简单封装
- 基于hibernate对数据库操作封装
- 实战学习NodeJS建站(4)—封装 SqlClient 实现对MySql的访问
- DAO层实现代码封装模板
- excel导出模板实现动态封装字段
- hibernate query分页封装
- Mysql对文件操作的封装
- mysql数据库对文件操作的封装
- MySQL 基于存储过程 实现数据统计按日、周、月份统计模板
- 对二叉树的基本操作的类模板封装
- 基于java实现,封装验证码实例
- JAVA工程对Solr进行增删改以及封装一个Query对象
- ThreadLocal对Connection封装的实现
- VC++中实现对Speex的封装
- 对进度条的通用封装实现
- 第一帖
- 谈优化JAVA程序开发,提高程序性能
- access语法大全
- STM32时钟控制RCC探究
- 分析Sqlserver与access数据库sql语法的10大差异
- 基于模板实现对MYSQL query封装
- 是谁在我的心里打了个结(五)检讨书
- mysql 性能优化方案
- 意法半导体stm32f
- jqGrid与Struts2的结合应用(五) —— 结合Action类进行数据查询
- 代码生成器工具之三
- 唯特科技再度对软件安全严格加强防范
- 行為規範
- 天行健,君子以自强不息;地势坤,君子以厚德载物 释意