MySQL++是一个针对MySQL C API的C++封装。它的目的是提供一个类似STL容易一样简单易用的接口,帮助你有效的避免在代码中使用复杂的SQL语句。

5.5. 更改数据

更改一个SSQLS数据和insert它一样的简单,下面是一个例子 examples/ssqls3.cpp

#include "cmdline.h"
#include "printdata.h"
#include "stock.h"
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
// 从命令行获取数据库参数
    const char* db = 0, *server = 0, *user = 0, *pass = "";
if (!parse_command_line(argc, argv, &db, &server, &user, &pass))
{
        return 1;
    }

try
{
        // 建立一个和数据库服务器的连接
        mysqlpp::Connection con(db, server, user, pass);
        // 创建一个查询
        mysqlpp::Query query = con.query(
                "select * from stock where item = \"Nürnberger Brats\"");
        // 保存查找结果,如果失败则抛出一个异常
        mysqlpp::StoreQueryResult res = query.store();
        if (res.empty()) {
            throw mysqlpp::BadQuery("UTF-8 bratwurst item not found in "
                    "table, run resetdb");
        }
 // 因为可能结果中只有一行数据,而我们的STL容器中没有保存指针。我们可以使用一个SSQLS保存第一行的stock对象
        stock row = res[0];
 // 创建一个副本,让我们记录下来需要更新的结果列有哪些
        stock orig_row = row;
 // 更换stock对象的item字段属性。
        row.item = "Nuerenberger Bratwurst";
 // 我们开始更新stock表内的指定结果行
        query.update(orig_row, row);
// 显示这个将被执行的查询语句
 

        cout << "Query: " << query << endl;
  // 因为执行这条查询不会有结果集需要返回,可以使用 execute()
        query.execute();
  // 输出新表内结构
        print_stock_table(query);
    }
    catch (const mysqlpp::BadQuery& er) {
        cerr << "Query error: " << er.what() << endl;
        return -1;
    }
    catch (const mysqlpp::BadConversion& er) {
        cerr << "Conversion error: " << er.what() << endl <<
                "\tretrieved data size: " << er.retrieved <<
                ", actual size: " << er.actual_size << endl;
        return -1;
    }
    catch (const mysqlpp::Exception& er) {
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    return 0;
}

在执行这个例子后不要忘记运行 resetdb 重置数据库。

5.6. 使用set容器保存SSQLS

如果我们需要对结果集进行一个大小排序,我们可能需要使用STL中的set容器。它默认定义了一个 operator < 操作符重载。SSQLS给予了这方面的支持,请参见例子 examples/ssqls4.cpp:

#include "cmdline.h"
#include "printdata.h"
#include "stock.h"
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    const char* db = 0, *server = 0, *user = 0, *pass = "";
    if (!parse_command_line(argc, argv, &db, &server, &user, &pass)) {
        return 1;
    }

    try {
        // 连接数据库服务器
        mysqlpp::Connection con(db, server, user, pass);

 // 取出stock表内所有行数据并保存在一个STL的set容器内。非常简单对吧,就和保存在 vector 中没什么两样,这是因为SSQLS对象都支持比较操作。
        mysqlpp::Query query = con.query("select * from stock");
        set<stock> res;
        query.storein(res);
 // 显示结果集
        print_stock_header(res.size());
        set<stock>::iterator it;
        cout.precision(3);
        for (it = res.begin(); it != res.end(); ++it) {
            print_stock_row(it->item.c_str(), it->num, it->weight,
                    it->price, it->sdate);
        }
// 使用set的find方法,根据一个item名称查找一个item。这里同样默认使用了SSQLS的比较操作。
        it = res.find(stock("Hotdog Buns"));
        if (it != res.end()) {
            cout << endl << "Currently " << it->num <<
                    " hotdog buns in stock." << endl;
        }
        else {
            cout << endl << "Sorry, no hotdog buns in stock." << endl;
        }
    }
    catch (const mysqlpp::BadQuery& er) {
        cerr << "Query error: " << er.what() << endl;
        return -1;
    }
    catch (const mysqlpp::BadConversion& er) {
        cerr << "Conversion error: " << er.what() << endl <<
                "\tretrieved data size: " << er.retrieved <<
                ", actual size: " << er.actual_size << endl;
        return -1;
    }
    catch (const mysqlpp::Exception& er) {
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    return 0;
}

因为在SSQLS内已经进行了声明,所以find()函数可以正常使用。我们可以重载这个函数。通常来说,我们进行两个SSQLS进行比对排序的时候,会取它的第一个字段进行比较。在数据库中,这个字段也

通常作为主键。因此,我们要注意数据库表内字段的设计,第一个字段作为主键更加合适。

5.7. 修改表名

如果你使用查询创建了一个SSQLS的表,默认情况下,数据库会创建一个和SSQLS结构类型名完全一致的一个表名。如果你觉得这样不合适,你可以这样修改一个表名:

stock::table(“MyStockData”);

你也可以在与创建表实例的时候修改实例名称:

stock s; s.instance_table(“AlternateTable”);

当你在一个多表数据库中创建SSQLS定义时很有用,能保证你各个表的名称不同。这个特性将保证你不得不为每个表创建一个不同名称的SSQLS。

严格来说,你仅仅需要在多线程项目中使用这个特性。如果在单线程中,你可以在使用每个SSQLS对象之前修改静态表名称,这很安全的。

5.8. 在多个模块中使用一个SSQLS

如果你需要在多个模块使用一个SSQLS结构,建议你可以在一个头文件中定义这个SSQLS结构,将会很方便的使用它。但是这样的话,你可能引发一些问题。因为每个SSQLS结构中都会保存一些静态常用数

据(例如表名和字段名列表),如果你在多个模块里使用#include包含这个头文件的话,你将会在链接时候得到一个重复定义的错误。 解决这个问题的方法是:你在所有引用模块中都定义一个预处理宏 MYSQLPP_SSQLS_NO_STATICS,但是保留其中一个文件不要定义该宏。如果定义了这个宏,将意味着在该宏之后的所有SSQLS内静态成员

变量将无效。 我们假设有一个文件 my_ssqls.h 内包含了 sql_create_N 宏,这个宏定义了一个 SSQLS.而这个SSQLS被至少两个模块使用。其中一个我们将 foo.cpp ,另外一个叫 my_ssqls.cpp 。我们可以随便选

择一个文件,认为它“拥有”了这个SSQLS,而另外一个文件,仅仅是“使用”了这个SSQLS。那么我们可以如下使用:

// 文件foo.cpp,我们假设它仅仅是使用了SSQLS,而没有拥有这个SSQLS #define MYSQLPP_SSQLS_NO_STATICS #include “my_ssqls.h”

// 文件 my_ssqls.cpp,我们假设它拥有了这个SSQLS,则它不需要额外的宏定义,只需要正常包含头文件即可 #include “my_ssqls.h”

如果我们不止两个模块使用了这个SSQLS,我们需要在大部分“使用”这个SSQLS的文件前增加额外的宏定义。此时我们可以有个更简单的方法。

// 文件 my_ssqls.h: #if !defined(EXPAND_MY_SSQLS_STATICS)

define MYSQLPP_SSQLS_NO_STATICS

#endif sql_create_X(Y, Z….) // SSQLS的定义

// 文件 foo.cpp, 一个普通的SSQLS“使用者” #include “my_ssqls.h”

// 文件 my_ssqls.cpp, 特殊的唯一的那个SSQLS“拥有者” #define EXPAND_MY_SSQLS_STATICS #include “my_ssqls.h”

这样我们可以免除对大量的“使用者”定义那个宏,只要“拥有者”定义一个宏即可。