从以前所介绍的SSQLS的介绍中咱们能够感觉到,SSQLS的精髓应该在sql_create_#这个宏,他所建立出来的这个结构体将会是突破的关键,因此我将会从如下顺序入手。html
1. sql_create_#mysql
先再来重复一下咱们须要使用的examples/stock.h中的内容linux
sql_create_6(
stock, 1, 6,
mysqlpp::sql_char, item,
mysqlpp::sql_bigint, num,
mysqlpp::sql_double, weight,
mysqlpp::sql_decimal, price,
mysqlpp::sql_date, sdate,
mysqlpp::Null<mysqlpp::sql_mediumtext>, description)
最直截了当的方式是直接去看这个宏的实现,可是做者也考虑到这个宏可能过于庞大,看起来不舒服(我看过一下,确实太麻烦了),因此在manual里面专门有一节是“Expanding SSQLS Macros”。经过如下语句能够看到sql_create_#到底作了什么(假设咱们要看的就是MYSQL++自带的那个emamples/stock.h这个头文件中定义的sql_create_#,且当前环境是linux,当前目录是MYSQL++源代码根目录)。sql
doc/ssqls-pretty < examples/stock.h > stock_expand.txt
当我打开这个stock_expand.txt以后我碉堡了,竟然有1w多行。仔细看一下,发现好可能是mysql的头文件里的东西,再回想一下做者在manual里面讲的话,说这个ssqls-pretty程序会调用编译器的preprocess的过程,而后把内容完整输出。去看一下这个stock.h,刚开始两行是#include <mysql++.h>,这也就对了,这个头文件但是包含了太多的其余头文件呀。想一想在编译原理里面学过的知识,全部的头文件的内容都被复制过来了,那不长才怪了呢。因此下面咱们就抓重点看。编程
在1w多行里面找重点其实也真不简单,线索是什么?先来回顾一下看到的stock.hapi
sql_create_6(
stock, 1, 6,
mysqlpp::sql_char, item,
mysqlpp::sql_bigint, num,
mysqlpp::sql_double, weight,
mysqlpp::sql_decimal, price,
mysqlpp::sql_date, sdate,
mysqlpp::Null<mysqlpp::sql_mediumtext>, description)
粗看看,只有stock这个class名字(也有多是struct),联想到SSQLS的用法(见以前的分析),线索只能是class stock或者struct stock。全局搜,果真查到了struct stock。ide
struct stock {
mysqlpp::sql_char item;
mysqlpp::sql_bigint num;
mysqlpp::sql_double weight;
mysqlpp::sql_double_null price;
mysqlpp::sql_date sDate;
mysqlpp::sql_mediumtext_null description;
...
}
显然,这个就是根据sql_create_#的几个变量直接生成的。函数
首先咱们先来验证一下以前在介绍sql_create_#的时候说的构造函数和set方法(为了看上去方便,我略微调整了代码顺序并把部分的实现放到了声明一块儿)。this
stock() : table_override_(0) {}
stock(const mysqlpp::Row& row)
: table_override_(0)
{
populate_stock<mysqlpp::sql_dummy>(this, row);
}
stock(const mysqlpp::sql_char &p1) : item (p1), table_override_(0) {}
stock(
const mysqlpp::sql_char &p1,
const mysqlpp::sql_bigint &p2,
const mysqlpp::sql_double &p3,
const mysqlpp::sql_double_null &p4,
const mysqlpp::sql_date &p5,
const mysqlpp::sql_mediumtext_null &p6) :
item (p1),
num (p2),
weight (p3),
price (p4),
sDate (p5),
description (p6),
table_override_(0)
{ }
void set(const mysqlpp::Row &row);
void set(const mysqlpp::sql_char &p1);
void set(
const mysqlpp::sql_char &p1,
const mysqlpp::sql_bigint &p2,
const mysqlpp::sql_double &p3,
const mysqlpp::sql_double_null &p4,
const mysqlpp::sql_date &p5,
const mysqlpp::sql_mediumtext_null &p6);
基本上和以前在介绍sql_create_#的时候所说的SETCOUNT和COMPCOUNT的用法相似。其实实现不用深究,无非就是按照你在sql_create_#的时候所设立的顺序逐个赋值而已。spa
根据上面的代码,咱们能够看到两个地方会有一些疑惑。
template <mysqlpp::sql_dummy_type dummy>
void populate_stock(stock *s, const mysqlpp::Row &row) {
mysqlpp::NoExceptions ignore_schema_mismatches(row);
s->item = row["item"].conv(mysqlpp::sql_char());
s->num = row["num"].conv(mysqlpp::sql_bigint());
s->weight = row["weight"].conv(mysqlpp::sql_double());
s->price = row["price"].conv(mysqlpp::sql_double_null());
s->sDate = row["sDate"].conv(mysqlpp::sql_date());
s->description = row["description"].conv(mysqlpp::sql_mediumtext_null());
}
仔细看这个模板函数的模板部分,这里声明了一个叫作mysqlpp::sql_dummy_type的东西,那是什么?若是你们仔细一点看,在这个模板方法里面其实并无用到这个模板实参。为了完整性,我仍是给你们翻出来了这个mysqlpp::sql_dummy_type的真面目,他被定义在了ssqls.h中
enum sql_dummy_type { sql_dummy };
因此说,这里并无什么特别的,只是先是搞一个NoExceptions防止这段代码抛出异常(是否还记得以前有介绍过MYSQL++支持两种编程方式,即C的返回值式和C++的异常式)。而后逐个转换而已。我以为,这里之因此要这个NoExceptions,根本缘由在于MYSQL++人为保障SSQLS结构和表的彻底对应关系应该是用户的责任。
table_override_是什么玩意儿,为何上面的代码里面一直把它设置为0?看过代码知道了,
const char* table_override_;
在通过查看这个变量的用法以后我发现了一点端倪——在MYSQL++之中,为了保证跨平台性,做者都使用0来代替咱们常常所使用的NULL。
那他是什么意思呢?来看一下最主要用到它的两个地方
const char* table() const { return table_override_ ? table_override_ : stock::table_; } void instance_table(const char* t) { table_override_ = t; }
以前有讲过,SSQLS有这样的一种功能,即用户能够将一个SSQLS结构体对应于同构的多张不一样名表。有两种办法能够修改这个代表,其中之一就是调用instance_table来修改全局表名,因而这个table_override_就是这个区别于默认的与SSQLS结构体同名的表名。
问题又来了,有没有看到上面的table()方法中用到了stock:: table_这个变量,从用法上来看这是已经静态变量,查了一下。
struct stock {
....
static const char* table_;
}
const char* stock::table_ = "stock";
sql_create_#宏里面定义了很多类型的比较函数(基本上涵盖了全部的比较符号),例如
bool operator == (const stock &other) const;
bool operator > (const stock &other) const;
实现方式也是比较统一,即便用sql_compare_stock函数
bool operator > (const stock &other) const {
return sql_compare_stock<mysqlpp::sql_dummy>(*this,other) > 0;
}
bool operator < (const stock &other) const {
return sql_compare_stock<mysqlpp::sql_dummy>(*this,other) < 0;
}
那么sql_compare_stock又是如何实现的
template <mysqlpp::sql_dummy_type dummy>
int sql_compare_stock(const stock &x, const stock &y) {
return mysqlpp::sql_cmp(x.item , y.item );
}
稍等一下,让咱们仔细来看一下这个sql_compare_stock,看那惟一的一句语句,sql_cmp的是什么?只是stock::item!为何只取了item这一个项?回忆一下,sql_create_#中有一个参数是COMPCOUNT,咱们在这里例子里面填写的但是1,也就是只有sql_create_#中第一个正式的参数才会被参与到比较,在这里不就是这个item嘛?那若是COMPCOUNT==2该怎么样?
template <mysqlpp::sql_dummy_type dummy>
int sql_compare_stock(const stock &x, const stock &y) {
int cmp;
cmp = mysqlpp::sql_cmp(x.item , y.item );
if (cmp) return cmp;
return mysqlpp::sql_cmp(x.num , y.num );
}
注意到,其实就是一个个进行比较而已。
再去找mysqlpp:: sql_cmp的实现,他们被定义在了ssqls.h中,其实这是一组override,也就是每一个类型都有本身的mysqlpp:: sql_cmp,例如:
inline int sql_cmp(const Time& a, const Time& b)
{
return a.compare(b);
}
inline int sql_cmp(signed char a, signed char b)
{
return a - b;
}
// 其余类型的比较方法
....
在打开宏以后,咱们看到在真正的SSQLS结构体被定制以前,被定义了不少类型。咱们仍是拿stock做为例子,先看到的就是一个根据咱们输入的sql_create_#所建立出来的enum。
enum stock_enum {
stock_item,
stock_num,
stock_weight,
stock_price,
stock_sDate,
stock_description ,
stock_NULL };
这个enum仍是相对比较简单,每一列都被定义了进去,最后还标记了一个结束标志stock_NULL,他的做用之后会看到。
接下去的是三组六个类型,分别是一个“全量”,一个“部份量”。
template <class Manip>
class stock_value_list {
public:
const stock* obj;
const char* delim;
Manip manip;
public:
stock_value_list (const stock* o, const char* d, Manip m) : obj(o), delim(d), manip(m) {}
};
template <class Manip>
class stock_cus_value_list {
public:
const stock* obj;
std::vector<bool> *include;
bool del_vector;
const char* delim;
Manip manip;
public:
~stock_cus_value_list () {
if (del_vector) delete include;
}
stock_cus_value_list (const stock* o, const char* d, Manip m, bool i1, bool i2, bool i3, bool i4, bool i5, bool i6);
stock_cus_value_list (const stock* o, const char* d, Manip m, stock_enum i1, stock_enum i2, stock_enum i3, stock_enum i4, stock_enum i5, stock_enum i6);
stock_cus_value_list (const stock* o, const char* d, Manip m ,std::vector<bool>* i)
: obj(o), include(i), del_vector(false), delim(d), manip(m) {}
};
固然,这里为了节约篇幅,只记录下value的相关类型,还有field,equal等的相关类型。
下表表示了各个类型表示的含义,其中“###”表示SSQLS的类型名,也就是上例中的stock。咱们假设当前的实例只有两个列“char(5) item”和“int num”,值分别是“abc”和“1”,当前的分隔符是“,”。
类型名字 |
含义 |
备注 |
###_value_list |
###这个SSQLS类型当前实例的依次全部列的值的列表 |
通过“os << 实例”后的效果 ‘abc’ ,1 |
###_cus_value_list |
###这个SSQLS类型当前实例的某些列的值的列表 |
利用std::vector<bool> *include所表示的位图表肯定哪些列是须要的 |
###_field_list |
###这个SSQLS类型当前实例的依次全部列的列名的列表 |
通过“os << 实例”后的效果 `item` ,`num` |
###_cus_ field _list |
###这个SSQLS类型当前实例的某些列的列名的列表 |
利用std::vector<bool> *include所表示的位图表肯定哪些列是须要的 |
###_equal_list |
###这个SSQLS类型当前实例的依次全部列的列名和其值所组成等号链接 |
通过“os << 实例”后的效果 `item` = ‘abc’, `num` = 1 |
###_cus_equal _list |
###这个SSQLS类型当前实例的某些列的列名的列表 |
利用std::vector<bool> *include所表示的位图表肯定哪些列是须要的 |
这几个类型基本上就是被当作struct来作的,没有不少花哨的方法,基本上就这么几个变量(仅以###_cus_value_list为例)。
const ###* obj; // ###变量的实例
std::vector<bool> *include; // 位图表,表示哪些列是须要使用的(true)
bool del_vector; // 忽略吧,就没见过true的
const char* delim; // 分隔符
Manip manip; // 这是个template parameter,其实就是用于作escaping和quoting的那些表示须要quote,escaping等的Enum。
那么哪些地方会构造他们?在SSQLS类型中,会有一系列的value_list,field_list,equal_list方法,每一个都有4个不一样的同构,异曲同工,你们最后都生成对应的###_XXX_list,###_cus_XXX_list类型。有些方法的签名带有默认值,以下
template <class Manip>
stock_cus_value_list<Manip> value_list(const char* d, Manip m, bool i1, bool i2 = false, bool i3 = false, bool i4 = false, bool i5 = false, bool i6 = false) const;
template <class Manip>
stock_cus_value_list<Manip> value_list(const char* d, Manip m, stock_enum i1, stock_enum i2 = stock_NULL, stock_enum i3 = stock_NULL, stock_enum i4 = stock_NULL, stock_enum i5 = stock_NULL, stock_enum i6 = stock_NULL) const;
显然,这两个签名其实含义是一致的,无非就是把false和enum的stock_NULL等价起来,而后就去写那个表明位图的vector<bool>。
构造出来了,谁去用它?全部的三组都是经过
ostream & operator << (ostream & s, ###_XXX_list<T> & obj);
ostream & operator << (ostream & s, ###_cus_XXX_list<T> & obj);
使用的。他们作了什么?各来看一个就懂了(以stock_value_list和stock_cus_value_list为例)
template <class Manip>
std::ostream& operator <<(std::ostream& s, const stock_value_list<Manip>& obj) {
s << obj.manip << obj.obj->item << obj.delim;
s << obj.manip << obj.obj->num << obj.delim;
s << obj.manip << obj.obj->weight << obj.delim;
s << obj.manip << obj.obj->price << obj.delim;
s << obj.manip << obj.obj->sDate << obj.delim;
s << obj.manip << obj.obj->description;
return s;
}
template <class Manip>
std::ostream& operator <<(std::ostream& s, const stock_cus_value_list<Manip>& obj)
{
bool before = false;
if ((*obj.include)[0]) {
s << obj.manip << obj.obj->item;
before = true;
}
if ((*obj.include)[1]) {
if (before) s << obj.delim;
s << obj.manip << obj.obj->num;
before = true;
}
// 这里我省略了其余的列
....
return s;
}
那定义了那么多与std::ostream相关的operator <<代码是用来作什么的?何时用value_list,何时用field_list,何时用equal_list?先透露点,value_list至少能够用做INSERT的VALUES内容,field_list至少能够用做INSERT的COLUMNS的地方,equal_list至少能够用在UPDATE的SET中。
2. SELECT至SSQLS
这个问题,咱们只从mysqlpp:: Query:: storein入手,其余的方法大体都同样的。即
mysqlpp::Query query = con.query("select item,description from stock");
vector<stock> res;
query.storein(res);
mysqlpp:: Query:: str()方法在template query里面讲过了,在上面的例子里,其实也就直接返回那句SELECT语句了。因为咱们给入的是vector<T>,因此直接进入到specified template里面。
接下去就方便了。经过mysqlpp:: Query:: use方法一条条提取数据,而后直接push_back到vector容器中。根据C++规则,在763行会调用copy构造函数,这里就是stock:: stock(Row &)这个构造函数。而后……我就不赘述了……
3. 更改SSQLS数据
这个问题,咱们只从mysqlpp:: Query:: insert和update这两个方法入手。
// 插入
stock row(“hello”, …);
mysqlpp::Query query = con.query();
query.insert(row);
query.execute();
// 更新
stock orig_row = row;
row.item = "Nuerenberger Bratwurst";
query.update(orig_row, row);
query.execute();
先来看表示插入的mysqlpp:: Query:: insert(),
首先,这个方法是一个模板方法。这也就意味着类型用多了,容易出现代码膨胀。
而后这里的重点也挺明显的,Query:: reset()方法状态给重置(具体参看template query部分),而后拼接处INSERT语句,注意看1012行的v.field_list()和1013行的v.value_list()方法。这两个是什么?就是咱们在sql_create_#最后花了大力气介绍的那些###_XXX_list啊。因此若是你是从头看下来的,必定很明白了。
再来看一下更新,
那个MYSQLPP_QUERY_THISPTR就看成是*this吧,回忆一下,Query继承自std::ostream。
原创做品,转载请注明出处www.cnblogs.com/aicro。