mysql conn sqlquery_【原创】3. MYSQL++ Query类型与SQL语句执行过程(非template与SSQLS版本)...

我们可以通过使用mysqlpp:: Query来进行SQL语句的增删改查。

首先来看一下mysqlpp::Query的一些最简单的调用,

conn.connect(mysqlpp::examples::db_name, "127.0.0.1", "root", "root");

mysqlpp::Query query = conn.query("select item from stock");

mysqlpp::StoreQueryResult res = query.store();

// OR

mysqlpp::Query query = conn.query("select * from stock");

{

mysqlpp::UseQueryResult res = query.use();

while (mysqlpp::Row row = res.fetch_row()) {

// row[0]; 第一列

}

}

在mysqlpp::Connection.query( )方法中,他其实是这样调用的。

11be998b7a454da3cd43b7da1065b47e.png

MYSQL++ 的mysqlpp::Query类型

121c78f4591f80f8a22b45ea3a9798f3.png

请注意,这里的Query居然继承了std: :ostream!这个是为什么?

我认为,这样做的一个好处是,可以运用如下的方式

mysqlpp::Query q(0);

q << mysqlpp::quote << “test”;

当然,mysqlpp::quote是一个enum【在mani.h中定义】,这里有很多技巧,放在专门的“escape与quote”相关内容中介绍。

用法

首先需要说明的是,通常情况下,APP并不会直接创建这个mysqlpp::Query对象,而是call mysqlpp::Connection::query() to get one tied to that connection.

该类型提供了一些能够执行select,insert等SQL语句的方法。有下面几种方法来执行一句SQL语句

pass a SQL statement in either the form of a C or C++ string to one of the exec*(),store*(),or use() methods

build up the query string over several C++ statements using Query's stream interface.

"template queries",This is something like C's printf() function. You call the parse() method to tell the Query object that the query string contains placeholders.

Use Specialized SQL Structures (SSQLS) to manipulate the db only by data structure.

Query有一些最基本的用法,包括

直接利用已经拼凑好了的SQL语句调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()

使用ostream接口,像构造cout一样构造SQL语句,然后再调用mysqlpp::Query::exec*(),mysqlpp::Query::store()或者mysqlpp::Query::use()

类似于printf一样使用template queries

类似于Hibernate那样使用Specialized SQL Structures feature(SSQLS)

这里只介绍最简单的两个情况,template queries和SSQLS的原理放到其他章节。

仔细来看一下Query提供的各个重要方法的重构方式(类似的方法族还有store)

首先关注参数

1)

UseQueryResult mysqlpp::Query::use (const char * str, size_t len )

该方法主要是我们已经有了一个已经”成熟“的SQL语句,类似于,我们在UI工具上已经打好了,然后贴过去的那种,这种语句必须要已经经过了escape等转义工作,MYSQL++内部并不做进一步的转义操作。

2)

UseQueryResult mysqlpp::Query::use ( const SQLTypeAdapter & str )

SQLTypeAdaper就是这个可以将普通的,未经过转义的SQL语句,或者如果“基本语句”(就是上面mysqlpp::Connection.query()传入的char*)是一个template query,那么可以将某个int,double等转义为可以供底层API所使用的string形式的工具类。(例如,如果update set a = 'sth' , 在这个sth周围就需要加上引号,而我们所传入的可能就是这个“sth”)

所以这个方法的工作就是将原始的SQL字符串 str 转换为可以供底层API所使用的字符串

3)

UseQueryResult mysqlpp::Query::use ( SQLQueryParms & p )

SQLQueryParms是一个为了template query而准备的PARAM相关的类型,例如,如果有类似于这样的sql template 语句(可能和实际的MYSQL++用法不一致,只是表意)

select * from where id = %d, name = ‘%s’.

则我们可以这样设置SQLQueryParms

SQLQueryParms sqp;

sqp << 1 << "root";

4)

UseQueryResult mysqlpp::Query::use ( )

这个方法就是使用“基本语句”进行逐条执行。

再来关注返回值

为了讲述方便,我们先来看一下在result.h(Declares classes for holding information about SQL query)中所定义的各种类型的关系。这几个类型不在这里做仔细介绍,可以查看相关章节。

d467645f60c10b69fffd19506b160902.png

1)

bool exec(const std::string& str);

这里返回的是bool,表示的是“语句是否执行成功”,我认为适合于那种update, delete,insert,且都不关心有多少rows被touch的情况。

2)

SimpleResult execute();

这里的SimpleResult正如其名,其中只有如下信息

the last value used for an AUTO_INCREMENT field

(ulonglong insert_id() const)

the number of rows affected by the query

(ulonglong rows( ) const)

any additional information about the query returned by the server(const char* info( ) const)

3)

UseQueryResult use();

由于use的语义类似于使用游标,也就是支持一行一行地拉出内容,所以UseQueryResult 也就自然而然地支持一些关于fetch row的功能。

4)

StoreQueryResult store();

StoreQueryResult它本身就是从vector继承而来,所以它就是vector。所以用户程序可以直接使用下标的形式来获取所有的ROW。这也就是说在这个store之后,所有的ROW的内容都在了这个vecor里面了。

for_each,store_if,storein,storein_set,storein_sequence

这些方法都是模仿STL中的里面的迭代器的处理函数,具体可以看看文档。

insert,update,insertfrom,replace,replacefrom

这些方法都是给SSQLS所使用的。

2. 实现

1) insert policy

首先我们看到了在mysqlpp::Query类型的一开始,就在private定义下面定义了一个#include

b33a98a7438dc18800600de51237f2f3.png

通过查看这个头文件,他定义了一系列的辅助类型,他们控制着在哪些条件下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或者template query)

我们假设有如下的调用Query代码

conn.connect(mysqlpp::examples::db_name, "127.0.0.1", "root", "root");

mysqlpp::Query query = conn.query("select item from stock");

mysqlpp::StoreQueryResult res = query.store();

mysqlpp::Query query = conn.query("select item from stock");

当代码进入到Connection::query的时候,该函数实际上在栈上新添了一个mysqlpp::Query对象,此时调用的构造函数是

d380753843c9fcc55ed68327f32c1574.png

需要注意的是两个变量

sbuffer,他是一个std::stringbuf,用来存储assembled query。另外,init和imbue方法都是std::ostream的方法。

template_defaults,这是一个SQLQueryParms,这个变量实际上视为template query做准备的,他用作“default template parameters, used for filling in parameterized queries”

注意看到上面的红色框中的内容

初始化列表中的内容都会在进入构造器之前完成,所以如果有类型在初始化列表中被构造(如下面的a(1),和上面例子中的template_defailt(this)),编译器都会在包含类型构造之前先行构造。(我原来以为,被一个包含类型只会被调用无参默认构造函数,其实可以在成员初始化列表中调用其他构造函数)

如果有如下代码

class A

{

public:

A(int i) { cout << "in cons A " << i << endl;}

}

class B

{

public:

B() : a(1) { cout << "in cons B" << endl;}

private:

A a;

}

输出结果将会是

in cons A 1

in cons B

根据上面的理论,template_defaults(this),实际上是调用了SQLQueryParms(Query * q)这个构造函数来构造SQLQueryParms.

其他的都是将sql语句加入到sbuffer中。但是需要注意的是上面提到的imbue用法,我们需要使用的是最通用的locale。

query.store( );

fc72bad783e25fe369ffd83e3169a773.png

AutoFlag其实就是一个RAII,在构造函数中将 template_defaults.processing_ 置为 true,在析构函数中将其置为false

Query: :store有以下四个不同的overload,具体的不同点请参看上文,以下四者殊途同归,最终调用的都是最后一个版本

StoreQueryResult store();

StoreQueryResult store(SQLQueryParms& p);

StoreQueryResult store(const SQLTypeAdapter& str);

StoreQueryResult store(const char* str, size_t len);

再来看一下Query: :str( SQLQueryParms)的源代码

9f1773c874bdccb88e5fb98decb55358.png

先解释一下什么是parse_elems_,他的定义是 std::vector parse_elems_; 表示 List of template query parameters

所以很显然,在我们的这一章节所关注的用法中,Query:: str( SQLQueryParms& )直接返回的就是sbuffer_.str( ),也就是构造Query时候所传入的SQL语句。

当回到Query:: store( )的时候,该方法直接调用的就是Query:: store(const SQLTypeAdapter& ),其中str的返回值(string)到SQLTypeAdapter是隐式转换并进行构造的(因为SQLTypeAdapter有一个以string为param的构造器)

2e8eefe8d6b3387ff6abfb4dd36d416b.png

该方法又会调用最为核心的store版本。StoreQueryResult store(const char* str, size_t len);(else里面的那句)

该版本其实也不难理解,

65de65b2287a401e3e94046fa1fcba94.png

523行到530行这一段是对template query的处理,这里不涉及,先略过。

从531行开始然后就是调用DbDriver的exec( )方法进行SQL语句执行,并通过DbDriver:: store_result( ) 方法获取表示结果集的 MYSQL_RES。

536行开始,针对res的结果进行处理,这里强调区分了SQL语句无返回值和SQL语句执行失败(res返回的是null,但是sql_error==0)两种情况。

对于非template query,需要做的是直接reset(调用Query:: reset( )),然后构造一个StoreQueryResult返回

最后来说一下这个Query:: reset( )函数

c25b81fc231cd94072947b2e6001f22c.png

该方法直接将ostream的buffer放空,并调整指针位置,重置所有的变量,防止下一次的执行情况和这一次的冲突。

最后需要说明的一点是,对于MYSQL C API而言,需要返回结果行的语句(如select)和不需要返回结果行的语句(如insert, delete)都是通过mysql_query( )函数进行的,所以其实在mysqlpp:: Query类型中,store( )和execute( )系列方法在最终执行上是一致的,即都是通过调用DbDriver:: execute(const char* qstr, size_t length)进行的。所以store( )和execute( )的唯一区别在于store( )其实最终返回的是带有结果的 StoreQueryResult类型,而execute( )返回的是 SimpleResult

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值