【原创】3. MYSQL++ Query类型与SQL语句执行过程(非template与SSQLS版本)
2014-03-30 09:52
726 查看
我们可以通过使用mysqlpp::Query来进行SQL语句的增删改查。
首先来看一下mysqlpp::Query的一些最简单的调用,
[code]
[/code]
在mysqlpp::Connection.query()方法中,他其实是这样调用的。
请注意,这里的Query居然继承了std::ostream!这个是为什么?
我认为,这样做的一个好处是,可以运用如下的方式
mysqlpp::Queryq(0);
q<<mysqlpp::quote<<“test”;
当然,mysqlpp::quote是一个enum【在mani.h中定义】,这里有很多技巧,放在专门的“escape与quote”相关内容中介绍。
用法
首先需要说明的是,通常情况下,APP并不会直接创建这个mysqlpp::Query对象,而是callmysqlpp::Connection::query()togetonetiedtothatconnection.
该类型提供了一些能够执行select,insert等SQL语句的方法。有下面几种方法来执行一句SQL语句
passaSQLstatementineithertheformofaCorC++stringtooneoftheexec*(),store*(),oruse()methods
buildupthequerystringoverseveralC++statementsusingQuery'sstreaminterface.
"templatequeries",ThisissomethinglikeC'sparse()methodtotelltheQueryobjectthatthequerystringcontainsplaceholders.
UseSpecializedSQLStructures(SSQLS)tomanipulatethedbonlybydatastructure.
Query有一些最基本的用法,包括
直接利用已经拼凑好了的SQL语句调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()
使用ostream接口,像构造cout一样构造SQL语句,然后再调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()
类似于printf一样使用templatequeries
类似于Hibernate那样使用SpecializedSQLStructuresfeature(SSQLS)
这里只介绍最简单的两个情况,templatequeries和SSQLS的原理放到其他章节。
仔细来看一下Query提供的各个重要方法的重构方式(类似的方法族还有store)
首先关注参数
1)
该方法主要是我们已经有了一个已经”成熟“的SQL语句,类似于,我们在UI工具上已经打好了,然后贴过去的那种,这种语句必须要已经经过了escape等转义工作,MYSQL++内部并不做进一步的转义操作。
2)
SQLTypeAdaper就是这个可以将普通的,未经过转义的SQL语句,或者如果“基本语句”(就是上面mysqlpp::Connection.query()传入的char*)是一个templatequery,那么可以将某个int,double等转义为可以供底层API所使用的string形式的工具类。(例如,如果updateseta='sth',在这个sth周围就需要加上引号,而我们所传入的可能就是这个“sth”)
所以这个方法的工作就是将原始的SQL字符串str转换为可以供底层API所使用的字符串
3)
SQLQueryParms是一个为了templatequery而准备的PARAM相关的类型,例如,如果有类似于这样的sqltemplate语句(可能和实际的MYSQL++用法不一致,只是表意)
select*fromwhereid=%d,name=‘%s’.
则我们可以这样设置SQLQueryParms
SQLQueryParmssqp;
sqp<<1<<"root";
4)
这个方法就是使用“基本语句”进行逐条执行。
再来关注返回值
为了讲述方便,我们先来看一下在result.h(DeclaresclassesforholdinginformationaboutSQLquery)中所定义的各种类型的关系。这几个类型不在这里做仔细介绍,可以查看相关章节。
1)
这里返回的是bool,表示的是“语句是否执行成功”,我认为适合于那种update,delete,insert,且都不关心有多少rows被touch的情况。
2)
这里的SimpleResult正如其名,其中只有如下信息
(ulonglonginsert_id()const)
(ulonglongrows()const)
anyadditionalinformationaboutthequeryreturnedbytheserver(constchar*info()const)
3)
由于use的语义类似于使用游标,也就是支持一行一行地拉出内容,所以UseQueryResult也就自然而然地支持一些关于fetchrow的功能。
4)
StoreQueryResult它本身就是从vector<Row>继承而来,所以它就是vector。所以用户程序可以直接使用下标的形式来获取所有的ROW。这也就是说在这个store之后,所有的ROW的内容都在了这个vecor<Row>里面了。
for_each,store_if,storein,storein_set,storein_sequence
这些方法都是模仿STL中的<algorithm>里面的迭代器的处理函数,具体可以看看文档。
insert,update,insertfrom,replace,replacefrom
这些方法都是给SSQLS所使用的。
2.实现
1)insertpolicy
首先我们看到了在mysqlpp::Query类型的一开始,就在private定义下面定义了一个#include
通过查看这个头文件,他定义了一系列的辅助类型,他们控制着在哪些条件下ROW是可以被插入的。文档中说,这些类型都是为SSQLS服务的(核心作用是在Query::insertfrom())
他们的核心方法是一个叫做can_add的方法
RowCountInsertPolicy——如果当前已经插入的rows查过了某个threshold,那么can_add就返回false
SizeThresholdInsertPolicy——如果当前想要插入的row的大小大于预设的threshold,那么can_add就返回false
MaxPacketInsertPolicy——如果当前已经插入的rows的总体的大小查过了某个threshold,那么can_add就返回false
2)最简单的实现流程(普通query,而非SSQLS或者templatequery)
我们假设有如下的调用Query代码
[code]mysqlpp::Queryquery=conn.query("selectitemfromstock");
[/code]
mysqlpp::Queryquery=conn.query("selectitemfromstock");
当代码进入到Connection::query的时候,该函数实际上在栈上新添了一个mysqlpp::Query对象,此时调用的构造函数是
需要注意的是两个变量
sbuffer,他是一个std::stringbuf,用来存储assembledquery。另外,init和imbue方法都是std::ostream的方法。
template_defaults,这是一个SQLQueryParms,这个变量实际上视为templatequery做准备的,他用作“defaulttemplateparameters,usedforfillinginparameterizedqueries”
注意看到上面的红色框中的内容
初始化列表中的内容都会在进入构造器之前完成,所以如果有类型在初始化列表中被构造(如下面的a(1),和上面例子中的template_defailt(this)),编译器都会在包含类型构造之前先行构造。(我原来以为,被一个包含类型只会被调用无参默认构造函数,其实可以在成员初始化列表中调用其他构造函数)
如果有如下代码
[code]{
[/code]
输出结果将会是
inconsA1
inconsB
根据上面的理论,template_defaults(this),实际上是调用了SQLQueryParms(Query*q)这个构造函数来构造SQLQueryParms.
其他的都是将sql语句加入到sbuffer中。但是需要注意的是上面提到的imbue用法,我们需要使用的是最通用的locale。
query.store();
AutoFlag其实就是一个RAII,在构造函数中将template_defaults.processing_置为true,在析构函数中将其置为false
Query::store有以下四个不同的overload,具体的不同点请参看上文,以下四者殊途同归,最终调用的都是最后一个版本
[code]StoreQueryResultstore(SQLQueryParms&p);
[/code]
再来看一下Query::str(SQLQueryParms)的源代码
先解释一下什么是parse_elems_,他的定义是std::vector<SQLParseElement>parse_elems_;表示Listoftemplatequeryparameters
所以很显然,在我们的这一章节所关注的用法中,Query::str(SQLQueryParms&)直接返回的就是sbuffer_.str(),也就是构造Query时候所传入的SQL语句。
当回到Query::store()的时候,该方法直接调用的就是Query::store(constSQLTypeAdapter&),其中str的返回值(string)到SQLTypeAdapter是隐式转换并进行构造的(因为SQLTypeAdapter有一个以string为param的构造器)
该方法又会调用最为核心的store版本。StoreQueryResultstore(constchar*str,size_tlen);(else里面的那句)
该版本其实也不难理解,
523行到530行这一段是对templatequery的处理,这里不涉及,先略过。
从531行开始然后就是调用DbDriver的exec()方法进行SQL语句执行,并通过DbDriver::store_result()方法获取表示结果集的MYSQL_RES。
536行开始,针对res的结果进行处理,这里强调区分了SQL语句无返回值和SQL语句执行失败(res返回的是null,但是sql_error==0)两种情况。
对于非templatequery,需要做的是直接reset(调用Query::reset()),然后构造一个StoreQueryResult返回
最后来说一下这个Query::reset()函数
该方法直接将ostream的buffer放空,并调整指针位置,重置所有的变量,防止下一次的执行情况和这一次的冲突。
最后需要说明的一点是,对于MYSQLCAPI而言,需要返回结果行的语句(如select)和不需要返回结果行的语句(如insert,delete)都是通过mysql_query()函数进行的,所以其实在mysqlpp::Query类型中,store()和execute()系列方法在最终执行上是一致的,即都是通过调用DbDriver::execute(constchar*qstr,size_tlength)进行的。所以store()和execute()的唯一区别在于store()其实最终返回的是带有结果的StoreQueryResult类型,而execute()返回的是SimpleResult
原创作品,转载请注明出处www.cnblogs.com/aicro。
首先来看一下mysqlpp::Query的一些最简单的调用,
conn.connect(mysqlpp::examples::db_name,"127.0.0.1","root","root");
[code]
mysqlpp::Queryquery=conn.query("selectitemfromstock");
mysqlpp::StoreQueryResultres=query.store();
//OR
mysqlpp::Queryquery=conn.query("select*fromstock");
{
mysqlpp::UseQueryResultres=query.use();
while(mysqlpp::Rowrow=res.fetch_row()){
//row[0];第一列
}
}
[/code]
在mysqlpp::Connection.query()方法中,他其实是这样调用的。
MYSQL++的mysqlpp::Query类型
请注意,这里的Query居然继承了std::ostream!这个是为什么?
我认为,这样做的一个好处是,可以运用如下的方式
mysqlpp::Queryq(0);
q<<mysqlpp::quote<<“test”;
当然,mysqlpp::quote是一个enum【在mani.h中定义】,这里有很多技巧,放在专门的“escape与quote”相关内容中介绍。
用法
首先需要说明的是,通常情况下,APP并不会直接创建这个mysqlpp::Query对象,而是callmysqlpp::Connection::query()togetonetiedtothatconnection.
该类型提供了一些能够执行select,insert等SQL语句的方法。有下面几种方法来执行一句SQL语句
passaSQLstatementineithertheformofaCorC++stringtooneofthe
buildupthequerystringoverseveralC++statementsusingQuery'sstreaminterface.
"templatequeries",ThisissomethinglikeC's
printf()function.Youcallthe
UseSpecializedSQLStructures(SSQLS)tomanipulatethedbonlybydatastructure.
Query有一些最基本的用法,包括
直接利用已经拼凑好了的SQL语句调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()
使用ostream接口,像构造cout一样构造SQL语句,然后再调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()
类似于printf一样使用templatequeries
类似于Hibernate那样使用SpecializedSQLStructuresfeature(SSQLS)
这里只介绍最简单的两个情况,templatequeries和SSQLS的原理放到其他章节。
仔细来看一下Query提供的各个重要方法的重构方式(类似的方法族还有store)
首先关注参数
1)
UseQueryResultmysqlpp::Query::use(constchar*str,size_tlen)
该方法主要是我们已经有了一个已经”成熟“的SQL语句,类似于,我们在UI工具上已经打好了,然后贴过去的那种,这种语句必须要已经经过了escape等转义工作,MYSQL++内部并不做进一步的转义操作。
2)
UseQueryResultmysqlpp::Query::use(constSQLTypeAdapter&str)
SQLTypeAdaper就是这个可以将普通的,未经过转义的SQL语句,或者如果“基本语句”(就是上面mysqlpp::Connection.query()传入的char*)是一个templatequery,那么可以将某个int,double等转义为可以供底层API所使用的string形式的工具类。(例如,如果updateseta='sth',在这个sth周围就需要加上引号,而我们所传入的可能就是这个“sth”)
所以这个方法的工作就是将原始的SQL字符串str转换为可以供底层API所使用的字符串
3)
UseQueryResultmysqlpp::Query::use(SQLQueryParms&p)
SQLQueryParms是一个为了templatequery而准备的PARAM相关的类型,例如,如果有类似于这样的sqltemplate语句(可能和实际的MYSQL++用法不一致,只是表意)
select*fromwhereid=%d,name=‘%s’.
则我们可以这样设置SQLQueryParms
SQLQueryParmssqp;
sqp<<1<<"root";
4)
UseQueryResultmysqlpp::Query::use()
这个方法就是使用“基本语句”进行逐条执行。
再来关注返回值
为了讲述方便,我们先来看一下在result.h(DeclaresclassesforholdinginformationaboutSQLquery)中所定义的各种类型的关系。这几个类型不在这里做仔细介绍,可以查看相关章节。
1)
boolexec(conststd::string&str);
这里返回的是bool,表示的是“语句是否执行成功”,我认为适合于那种update,delete,insert,且都不关心有多少rows被touch的情况。
2)
SimpleResultexecute();
这里的SimpleResult正如其名,其中只有如下信息
thelastvalueusedforanAUTO_INCREMENTfield
(ulonglonginsert_id()const)
thenumberofrowsaffectedbythequery
(ulonglongrows()const)
anyadditionalinformationaboutthequeryreturnedbytheserver(constchar*info()const)
3)
UseQueryResultuse();
由于use的语义类似于使用游标,也就是支持一行一行地拉出内容,所以UseQueryResult也就自然而然地支持一些关于fetchrow的功能。
4)
StoreQueryResultstore();
StoreQueryResult它本身就是从vector<Row>继承而来,所以它就是vector。所以用户程序可以直接使用下标的形式来获取所有的ROW。这也就是说在这个store之后,所有的ROW的内容都在了这个vecor<Row>里面了。
for_each,store_if,storein,storein_set,storein_sequence
这些方法都是模仿STL中的<algorithm>里面的迭代器的处理函数,具体可以看看文档。
insert,update,insertfrom,replace,replacefrom
这些方法都是给SSQLS所使用的。
2.实现
1)insertpolicy
首先我们看到了在mysqlpp::Query类型的一开始,就在private定义下面定义了一个#include
通过查看这个头文件,他定义了一系列的辅助类型,他们控制着在哪些条件下ROW是可以被插入的。文档中说,这些类型都是为SSQLS服务的(核心作用是在Query::insertfrom())
他们的核心方法是一个叫做can_add的方法
RowCountInsertPolicy——如果当前已经插入的rows查过了某个threshold,那么can_add就返回false
SizeThresholdInsertPolicy——如果当前想要插入的row的大小大于预设的threshold,那么can_add就返回false
MaxPacketInsertPolicy——如果当前已经插入的rows的总体的大小查过了某个threshold,那么can_add就返回false
2)最简单的实现流程(普通query,而非SSQLS或者templatequery)
我们假设有如下的调用Query代码
conn.connect(mysqlpp::examples::db_name,"127.0.0.1","root","root");
[code]mysqlpp::Queryquery=conn.query("selectitemfromstock");
mysqlpp::StoreQueryResultres=query.store();
[/code]
mysqlpp::Queryquery=conn.query("selectitemfromstock");
当代码进入到Connection::query的时候,该函数实际上在栈上新添了一个mysqlpp::Query对象,此时调用的构造函数是
需要注意的是两个变量
sbuffer,他是一个std::stringbuf,用来存储assembledquery。另外,init和imbue方法都是std::ostream的方法。
template_defaults,这是一个SQLQueryParms,这个变量实际上视为templatequery做准备的,他用作“defaulttemplateparameters,usedforfillinginparameterizedqueries”
注意看到上面的红色框中的内容
初始化列表中的内容都会在进入构造器之前完成,所以如果有类型在初始化列表中被构造(如下面的a(1),和上面例子中的template_defailt(this)),编译器都会在包含类型构造之前先行构造。(我原来以为,被一个包含类型只会被调用无参默认构造函数,其实可以在成员初始化列表中调用其他构造函数)
如果有如下代码
classA
[code]{
public:
A(inti){cout<<"inconsA"<<i<<endl;}
}
classB
{
public:
B():a(1){cout<<"inconsB"<<endl;}
private:
Aa;
}
[/code]
根据上面的理论,template_defaults(this),实际上是调用了SQLQueryParms(Query*q)这个构造函数来构造SQLQueryParms.
其他的都是将sql语句加入到sbuffer中。但是需要注意的是上面提到的imbue用法,我们需要使用的是最通用的locale。
query.store();
AutoFlag其实就是一个RAII,在构造函数中将template_defaults.processing_置为true,在析构函数中将其置为false
Query::store有以下四个不同的overload,具体的不同点请参看上文,以下四者殊途同归,最终调用的都是最后一个版本
StoreQueryResultstore();
[code]StoreQueryResultstore(SQLQueryParms&p);
StoreQueryResultstore(constSQLTypeAdapter&str);
StoreQueryResultstore(constchar*str,size_tlen);
[/code]
再来看一下Query::str(SQLQueryParms)的源代码
先解释一下什么是parse_elems_,他的定义是std::vector<SQLParseElement>parse_elems_;表示Listoftemplatequeryparameters
所以很显然,在我们的这一章节所关注的用法中,Query::str(SQLQueryParms&)直接返回的就是sbuffer_.str(),也就是构造Query时候所传入的SQL语句。
当回到Query::store()的时候,该方法直接调用的就是Query::store(constSQLTypeAdapter&),其中str的返回值(string)到SQLTypeAdapter是隐式转换并进行构造的(因为SQLTypeAdapter有一个以string为param的构造器)
该方法又会调用最为核心的store版本。StoreQueryResultstore(constchar*str,size_tlen);(else里面的那句)
该版本其实也不难理解,
523行到530行这一段是对templatequery的处理,这里不涉及,先略过。
从531行开始然后就是调用DbDriver的exec()方法进行SQL语句执行,并通过DbDriver::store_result()方法获取表示结果集的MYSQL_RES。
536行开始,针对res的结果进行处理,这里强调区分了SQL语句无返回值和SQL语句执行失败(res返回的是null,但是sql_error==0)两种情况。
对于非templatequery,需要做的是直接reset(调用Query::reset()),然后构造一个StoreQueryResult返回
最后来说一下这个Query::reset()函数
该方法直接将ostream的buffer放空,并调整指针位置,重置所有的变量,防止下一次的执行情况和这一次的冲突。
最后需要说明的一点是,对于MYSQLCAPI而言,需要返回结果行的语句(如select)和不需要返回结果行的语句(如insert,delete)都是通过mysql_query()函数进行的,所以其实在mysqlpp::Query类型中,store()和execute()系列方法在最终执行上是一致的,即都是通过调用DbDriver::execute(constchar*qstr,size_tlength)进行的。所以store()和execute()的唯一区别在于store()其实最终返回的是带有结果的StoreQueryResult类型,而execute()返回的是SimpleResult
原创作品,转载请注明出处
相关文章推荐
- MySQL执行SQL语句过程详解
- mysql怎么创建可以定时执行任务的过程语句存储过程定时执行sql
- MySQL存储过程中实现执行动态SQL语句的方法
- MySQL存储过程详解 mysql 存储过程mysql存储过程详解 1. 存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored
- MySql带参数的存储过程编写(动态执行SQL语句)
- mysql版本不同所导致SQL语句执行错误的问题
- MYSQL存储过程执行用字符串拼成的sql语句
- mysql存储过程执行动态sql语句
- MySQL 存储过程中执行动态 SQL 语句
- MySQL 存储过程中执行动态SQL语句的方法
- mysql存储过程执行动态sql语句
- MySql带参数的存储过程编写(动态执行SQL语句)
- mysql 存储过程动态执行sql语句
- 执行字符串SQL语句--带有参数的存储过程以及 int类型的字符串变量注意事项
- 3.笔记 MySQL学习——简单执行SQL语句
- MySQL存储过程中实现执行动态SQL语句的方法
- mysql 存储过程 根据参数 动态执行sql语句
- mysql(1)—— 详解一条sql语句的执行过程
- MySql带参数的存储过程编写(动态执行SQL语句)
- mysql中SQL执行过程详解与用于预处理语句的SQL语法