zoukankan      html  css  js  c++  java
  • 【原创】13. MYSQL++之SSQLS(基本用法篇)

    1. 综述

    终于来到了SSQLS( Specialized SQL Structure),照我看来这是一个很类似于Hibernate的功能。也就是说,通过SSQLS可以将一张表完全对应到一个C++结构体中,然后只要对于这个结构体进行操作,同时再加入到对应的mysqlpp:: Query对应的方法中,我们就可以执行对应的SQL语句。

    值得说一下的是,该功能在实际项目中还是相当有用的,但是笔者所在公司由于其业务的特殊性,通常来说表的结构会非常非常大(有一些表居然有近200个字段)。根据作者的manual,MYSQL++默认只支持不超过25个字段的表结构,但是可以通过在编译前调整一些参数来修改这个限制。于是笔者进行了一下测试,通过这里的说法,修改为200个字段,然后编译整个MYSQL++重建,发现不是一般的卡,笔者刚开始以为,只是在编译MYSQL++的时候会比较卡,但是实际上,每一个运用了MYSQL++的程序都在编译时非常的卡!原因?后面你会看到的。

    最后来说一下SSQLS的限制,其实编译慢也就算了,他最大的缺点是源自于C++的劣根性。我们知道,如果我们申明了一个变量int i,如果没有为其初始化,他的值就是个乱码。由于在SSQLS的“表-结构”对应中也有可能会用到int, double,long这种最原始的数据类型,所以也就存在这个问题,即,他没有办法为你赋初值的(但是由于SQL表中的varchar和char,在MYSQL++中用std::string表示的,所以字符类型没有初始化问题)。笔者为此吃过不少亏,因为在执行SQL语句的时候(利用SSQLS做一句insert)返回了一个语法错误!原来笔者的表里面有一些double,然后由于内存垃圾的缘故,导致了这个double被写成了1.23E10之类的,在insert的时候需要一个数字的地方却有了这么个东西,于是返回出错。退一万步来说,即使没有这个返回错误,这样的表你敢用吗?

    2. 用法

    老规矩,在分析原理之前,我们先来看一下如何用。最好的文档还是manual和示例程序。

    • ”表与结构体映射”宏sql_create_#

    如果我们有这样的表

    CREATE TABLE stock (
     
    item CHAR(30) NOT NULL,
     
    num BIGINT NOT NULL,
     
    weight DOUBLE NOT NULL,
     
    price DECIMAL(6,2) NOT NULL,
     
    sdate DATE NOT NULL,
     
    description MEDIUMTEXT NULL)
     

    为了使用SSQLS,我们需要对此表“类型化”,如何做?使用作者提供的sql_create_#宏。

    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)

    当然,这里需要提供一个除了“mysql++.h”之外的另外一个头文件ssqls.h才能够用这个宏。他具体是怎么实现的我们先不管,我们主要关注他怎么用。

     

    sql_create_#(NAME, COMPCOUNT, SETCOUNT, TYPE1, ITEM1, ... TYPE#, ITEM#)

    首先这个#表示这个表中有多少字段。NAME你就把它当做是表名(当然后面会提到这么一种情况:我有两张表结构一模一样的表格,主要用来互换,这里需要sql_create_#两次?显然大牛老外没有这么蠢,他提供了一些方法来让一个结构体为多张同构不同名表服务的机制)。COMPCOUNT是什么?这个参数的意思就是从第一个字段开始之后的COMPCOUNT个字段共同参与了比较。这个机制在有主键的表中特别合适,比如上面的例子中,item自己就形成了“主键”。SETCOUNT是从第一个字段开始之后的SETCOUNT个字段会行程构造器的参数,和类型内的set方法的参数(由sql_create_#创建出来的类型一定会有四个构造函数,分别是无参数版本,仅包含主键版本——即COMPCOUNT所指定的数目,仅有const mysqlpp:: Row&的版本,以及包含有SETCOUNT所指定的数目的参数的构造函数。该类型也会有3个set方法,分别是以单参数const mysqlpp::Row,以主键类型为参数,以SETCOUNT所指定的数目和顺序为参数的版本)。例如

    sql_create_6(
        some_table, 1, 3,    
        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) 
     
     
    some_table foo;
    some_table foo(row);         //这个row是mysqlpp:: Row类型
    some_table foo(“Hotdog”); 
    some_table foo("Hotdog", 52, 20);
     
    foo.set(row);
    foo.set("hello");
    foo.set("hello", 500, 300);

    在manual中还提到过由于C++的语法不可以将COMPCOUNT和SETCOUNT写为同一个数字,work around是为SETCOUNT设置0,即提示MYSQL++不要生成对应的构造器,具体原理下面讲。

    • 获取数据
    mysqlpp::Query query = con.query("select item,description from stock");
     
    vector<stock> res;
     
    query.storein(res);
     
    vector<stock>::iterator it = res.begin();
     
    cout << it->description;
     

    或者,如果我们知道获取到的数据只有一条记录,那么可以不用存储在vector里面。

    mysqlpp::Query query = con.query("select item,description from stock limit 1 ");
    
    mysqlpp::StoreQueryResult res = query.store();
    
    stock row = res[0];
    
    

    用起来还是很方便的,和Hibernate很像。我们看到,上面的select语句只抽取item和description两个字段,然后通过mysqlpp:: Query的与SSQLS相关的方法吧结果防止到vector<stock>这个容器中,随后就像普通类型一样用。

    有一点需要注意的是,这里只用到了两个字段,所以我们大可在sql_create_#中只用声明两个字段,即使stock表有6个字段之多(见sql_create_#的相关说明)。MYSQL++非常智能,他会根据SELECT出来的字段填充同名类型字段,如果没有这个同名类型字段,MYSQL++则会直接忽略它。

    值得一提的是,作者在manual中解释了为什么要这么设计,貌似是因为大家把MYSQL都当做是分布式数据库来用,所以对于schema的改动可能会有先后。所以要让MYSQL++尽量高可拓展。

    • 增加数据
    stock row("Hot Dogs", 100, 1.5, …);
     
    mysqlpp::Query query = con.query( );
     
    query.insert(row).execute( );
     

    或者

    stock row("Hot Dogs", 100, 1.5, …);
     
    vector<stock> rows;
     
    rows.push_back(row);
     
    mysqlpp::Query query = con.query( );
     
    query.insert(rows.begin( ), rows.end( )) .execute( );
     

    基本语法还是很直观的,和声明你要插入的表的对应类型的实例,然后调用mysqlpp:: Query中与SSQLS相关的方法接口就可以了。

    但是,作者在这里重点突出了一个地方,那就是insert policy。我记得我在介绍mysql:: Query源代码的时候就说过这个东西,他在源代码中是以直接在private域里面做#include的等方式加入的。

    clip_image001

    那么,这个insert policy在什么地方会用到?

    待会我们就会看到,如果某个vector里面有两个元素(分别为row1和row2),那么在Query:: insert(sth.begin(), sth.end() )中,实际上会把一句insert语句拓展成(意会而已)

    “insert into XXX(…) values (row1), (row2)”

    然后作者提到说,MySQL has a limit on the size of the SQL query it will process.这就暗示我们了一种情况,如果这个vector里面有几百万个对象,展开的SQL语句超过了MYSQL对于query的limit,那么这句语句必然失败。显然,更改MYSQL的这个limit换汤不换药。那么,更好的办法是什么?

    我们可以设置某些“门槛”,一旦query的长度(或者涉及到的条数,或者某个对象的大小)超过了这个“门槛”,那么就截断这条query,让他送出去。剩下的继续来生成下一条query语句。

    MYSQL++就是采用这个insert policy机制来实现它的。

    mysqlpp::Query::MaxPacketInsertPolicy<> insert_policy(1000);
     
    query.insertfrom(stock_vector.begin(), stock_vector.end(), insert_policy);
     

    有哪些insert policy?

    Insert Policy

    说明

    MaxPacketInsertPolicy

    如果当前已经插入的rows的总体的大小查过了某个threshold,那么can_add就返回false

    SizeThresholdInsertPolicy

    如果当前想要插入的row的大小大于预设的threshold,那么can_add就返回false。即放弃那些row的大小超过某个值的数据行。当你的数据内每一行都比较短小的时候(例如没有BLOB数据字段),使用该限制对象,将使你的插入更加高效和合理。

    RowCountInsertPolicy

    如果当前已经插入的rows的数目超过了某个threshold,那么can_add就返回false

    • 修改数据
    // 先获取一条出来
     
    mysqlpp::Query query = con.query("select item,description from stock limit 1 ");
     
    mysqlpp::StoreQueryResult res = query.store();
     
    stock row = res[0];
     
    row.item = “abc”;
     
    query.update(orig_row, row);
     
    query.execute();
     
     
    • 删除数据

    看了一下mysqlpp:: Query的源代码,貌似没有发现有和update类似的delete方法,所以我估计,在MYSQL++中,如果需要删除一些数据,也只能够通过以下方式进行

    mysqlpp::Query query = con.query("delete from stock where … ");
     
    query.execute();
     
    • 当表名与结构体名不一致时

    记得在讲sql_create_#宏的时候,第一个参数就是该结构体和其所对应的表名(两者一般是一样的)。但是在我所在的公司,由于业务上的需要,通常一张表(以下假设该表名为stock)可能会有两个版本,即第一天用stock1,第二天用stock2。这种情况下,我们该怎么办?显然使用sql_create_#两次来创建两个一模一样的结构体是一种办法。但是效率显然是低下的,即使在数据库中,为了实现这个功能我们确实需要创建两个一模一样的表。但是在程序中,一个同构stock结构体就可以解决这些事情了。

    根据默认的情况,SSQLS默认结构体的名字就是你所需要找的表的名字。那么上面的情况怎么处理?

    方法一是全局修改表名,使得这个SSQLS结构体能够匹配特定的表。

    stock::table(“stock1”);

    这里的stock是SSQLS结构体。

    方法二是针对不同的实例(instance)都修改对应的表。例如,

    stock s1;
    
    s1.instance_table("stock1");
    
    stock s2;
    
    s1.instance_table("stock2");
    
    

    这样插入s1这个实例就会插向stock1, 插入s2这个实例就会插向stock2。

    • 当把SSQLS结构类型当做基类

    我们确实可以把一个SSQLS结构类型当做基类型使用,但是为了确保你所派生出来的类型他还是一个SSQLS类型,你必须要把所有被sql_create_#所隐藏了的构造函数都显式地列出来,这是因为SSQLS需要使用这些构造函数(还记得不,在介绍sql_create_#的时候有说过这种类型一定有4个构造函数),如果我们没有这些构造函数,那么如果将该类型作为SSQLS所使用,就会出现编译错误。同时不要去试图override那些需要被MYSQL++ internal use的方法。

    下面是manual上的一个派生例子。

    sql_create_2(  
        Base, 1, 2,  
        mysqlpp::sql_varchar, a,  
        mysqlpp::sql_int, b); 
     
    class Derived : public Base
    {
        public:  
        // default constructor
        Derived() :  Base()  {  }   
     
        // for-comparison constructor
        Derived(const mysqlpp::sql_varchar& _a) :  Base(_a)  {  }   
     
        // full creation constructor  
        Derived(const mysqlpp::sql_varchar& _a, const mysqlpp::sql_int& _b) :  Base(_a, _b)  {  }   
     
        // population constructor
        Derived(const mysqlpp::Row& row) :  Base(row)  {  }   
        
     
        // functionality added to the SSQLS through inheritance  
        bool do_something_interesting(int data);
    };

    原创作品,转载请注明出处www.cnblogs.com/aicro

  • 相关阅读:
    PHP Notice: Undefined index:解决方法
    javascript监听手机返回键
    jquery判断手指滑动方向
    php 5.5使用 array_column的方法
    html5 点击播放video的方法
    mysql并发量过大造成 update语句更新错误
    html5 微信真机调试方法vConsole
    PHP防止客户端多次点击
    jquery设置html5音量的方法
    设计模式六大原则
  • 原文地址:https://www.cnblogs.com/aicro/p/3633520.html
Copyright © 2011-2022 走看看