zoukankan      html  css  js  c++  java
  • MySQL Connector/C++入门教程(上)

    转载原文:http://blog.csdn.net/jgood/article/details/5661339

    原文地址: http://dev.mysql.com/tech-resources/articles/mysql-connector-cpp.html#trx

    翻译: DarkBull(www.darkbull.net)
    示例代码:MySqlDemo.7z
    译者注:该教程是一篇介绍如何使用C++操作MySQL的入门教程,内容简单易用。我对原文中的一些例子进行了修改,并新添加了部分例子,主要目标是更简单明了的向读者介绍如何操作MySQL数据库。本人也是MySQL的初学者,错误也在所难免,欢迎拍砖!
     

        这篇教程将一步一步引导您如何去构建和安装MySql Connection/C++ Driver,同时提供几个简单的例子来演示如何连接MySQL数据库,如何向MySQL添加、获取数据。本教程关注如何在C++应用程序中操作MySQL,所以首先应该确定MySQL数据库服务已经开启并且在当前机器能够访问到。

        本教程面向的读者是MySQL Connector/C++的初学者,如果您对C++语言或者MySQL数据库不是很了解,请参考其他的教程。
        教程使用了下面所列的一些工具和技术,来构建、编译、运行例子程序(译者注:这是原文作者使用的环境。笔者使用的环境是:WinXP,MySQL5.1,VS2008, ):
    • DatabaseMySQL Server 5.1.24-rc
    • C++ DriverMySQL Connector/C++ 1.0.5
    • MySQL Client LibraryMySQL Connector/C 6.0
    • CompilerSun Studio 12 C++ compiler
    • MakeCMake 2.6.3
    • Operating SystemOpenSolaris 2008.11 32-bit
    • CPU / ISAIntel Centrino / x86
    • HardwareToshiba Tecra M2 Laptop
     

    目录

    MySQL C++ Driver的实现基于JDBC4.0规范
    安装MySQL Connector/C++
    运行时依赖
    C++ IDE

    为示例程序创建数据库与数据表

    使用Connector/C++测试数据库连接
    使用prepared Statements
    使用事务
    访问Result Set Metadata
    访问Database Metadata
    通过PreparedStatment对象访问参数元数据

    捕获异常

    调试/跟踪 MySQL Connector/C++
    更多信息
     

    MySQL C++ Driver的实现基于JDBC4.0规范

        MySQL Connector/C++是由Sun Microsystems开发的MySQL连接器。它提供了基于OO的编程接口与数据库驱动来操作MySQL服务器。
        与许多其他现存的C++接口实现不同,Connector/C++遵循了JDBC规范。也就是说,Connector/C++ Driver的API主要是基于Java语言的JDBC接口。JDBC是java语言与各种数据库连接的标准工业接口。Connector/C++实现了大部分JDBC4.0规范。如果C++程序的开发者很熟悉JDBC编程,将很快的入门。
        MySQL Connector/C++实现了下面这些类:
    • Driver
    • Connection
    • Statement
    • PreparedStatement
    • ResultSet
    • Savepoint
    • DatabaseMetaData
    • ResultSetMetaData
    • ParameterMetaData
        Connector/C++可用于连接MySQL5.1及其以后版本。
        在MySQL Connector/C++发布之前,C++程序员可以使用MySQL C API或者MySQL++访问MySQL。前者是非标准、过程化的C API,后者是对MySQL C API的C++封装。

    安装MySQL Connector/C++

        此处略。(译者注:用户可以到MySQL的官网[http://dev.mysql.com/downloads/connector/cpp/1.0.html]去下载MySQL Connector/C++的安装程序,或者只下载dll,或者下载源代码自己编译。笔者在Window平台上使用MySQL,下载了mysql-connector-c++-noinstall-1.0.5-win32这个版本用于调试。)

    运行时依赖

        MySQL Connector/C++ Driver依赖MySQL的客户端库,在MySQL安装目录下的lib/opt/libmysql.dll。如果是通过安装程序来安装MySQL Connector/C++,libmysql会一并安装,如果从官网只下载了dll或源码,在使用时,程序必须链接到libmysql.dll。

    C++ IDE

        此处略。(译者注:原文作者使用NetBean作为C++的IED。笔者使用VS2008)

    为示例程序创建数据库与数据表

        (译者注:此节略掉许多不太重要的内容。)在MySQL中创建test数据库,使用下面语句创建数据表:City:
    1. Create TableCREATE TABLE `City` ( `CityName` varchar(30) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=ascii   
        然后向City表中添加一些数据。最后表的内容为:
    mysql> SELECT * FROM City; 
     +--------------------+ 
     | CityName           | 
     +--------------------+ 
     | Hyderabad, India   | 
     | San Francisco, USA |
     | Sydney, Australia  |
     +--------------------+
     3 rows in set (0.17 sec) 

    使用Connector/C++测试数据库连接

        下面的代码演示如何使用Connector/C++连接到MySQL服务器:

    •     连接到test数据库;
    •     执行一个查询获取City表中的数据,显示在控制台上; 
    •     使用Prepared Statements向City表插入数据;
    •     使用savepoints演示事务;
    •     获取结果集和数据库的元信息;
        例子代码仅仅用于演示,不建议读者在实际开发中使用这种样式的代码。(译者注:例子代码很长,如果看不太明白,没关系,等阅读完全文之后再回过头来看)
    1.                 #include <iostream>  
    2. #include <map>  
    3. #include <string>  
    4. #include <memory>  
    5.   
    6. #include "mysql_driver.h"  
    7. #include "mysql_connection.h"  
    8. #include "cppconn/driver.h"  
    9. #include "cppconn/statement.h"  
    10. #include "cppconn/prepared_statement.h"  
    11. #include "cppconn/metadata.h"  
    12. #include "cppconn/exception.h"  
    13.   
    14. #define DBHOST "tcp://127.0.0.1:3306"  
    15. #define USER "root"  
    16. #define PASSWORD "000000"  
    17. #define DATABASE "test"  
    18.   
    19. #define NUMOFFSET 100  
    20. #define COLNAME 200  
    21.   
    22. using namespace std;  
    23. using namespace sql;  
    24.   
    25. #pragma comment(lib, "mysqlcppconn.lib")  
    26.   
    27. void Demo();  
    28.   
    29. int main(int argc, char *argv[])  
    30. {  
    31.     Demo();  
    32.   
    33.     return 0;  
    34. }  
    35.   
    36. /* 获取数据库信息 */  
    37. static void GetDBMetaData(Connection *dbcon)   
    38. {  
    39.     if (dbcon->isClosed())   
    40.     {  
    41.         throw runtime_error("DatabaseMetaData FAILURE - database connection closed");  
    42.     }  
    43.   
    44.     cout << "/nDatabase Metadata" << endl;  
    45.     cout << "-----------------" << endl;  
    46.   
    47.     cout << boolalpha;  
    48.   
    49.     /* The following commented statement won't work with Connector/C++ 1.0.5 and later */  
    50.     //auto_ptr < DatabaseMetaData > dbcon_meta (dbcon->getMetaData());  
    51.   
    52.     DatabaseMetaData *dbcon_meta = dbcon->getMetaData();  
    53.   
    54.     cout << "Database Product Name: " << dbcon_meta->getDatabaseProductName() << endl;  
    55.     cout << "Database Product Version: " << dbcon_meta->getDatabaseProductVersion() << endl;  
    56.     cout << "Database User Name: " << dbcon_meta->getUserName() << endl << endl;  
    57.   
    58.     cout << "Driver name: " << dbcon_meta->getDriverName() << endl;  
    59.     cout << "Driver version: " << dbcon_meta->getDriverVersion() << endl << endl;  
    60.   
    61.     cout << "Database in Read-Only Mode?: " << dbcon_meta->isReadOnly() << endl;  
    62.     cout << "Supports Transactions?: " << dbcon_meta->supportsTransactions() << endl;  
    63.     cout << "Supports DML Transactions only?: " << dbcon_meta->supportsDataManipulationTransactionsOnly() << endl;  
    64.     cout << "Supports Batch Updates?: " << dbcon_meta->supportsBatchUpdates() << endl;  
    65.     cout << "Supports Outer Joins?: " << dbcon_meta->supportsOuterJoins() << endl;  
    66.     cout << "Supports Multiple Transactions?: " << dbcon_meta->supportsMultipleTransactions() << endl;  
    67.     cout << "Supports Named Parameters?: " << dbcon_meta->supportsNamedParameters() << endl;  
    68.     cout << "Supports Statement Pooling?: " << dbcon_meta->supportsStatementPooling() << endl;  
    69.     cout << "Supports Stored Procedures?: " << dbcon_meta->supportsStoredProcedures() << endl;  
    70.     cout << "Supports Union?: " << dbcon_meta->supportsUnion() << endl << endl;  
    71.   
    72.     cout << "Maximum Connections: " << dbcon_meta->getMaxConnections() << endl;  
    73.     cout << "Maximum Columns per Table: " << dbcon_meta->getMaxColumnsInTable() << endl;  
    74.     cout << "Maximum Columns per Index: " << dbcon_meta->getMaxColumnsInIndex() << endl;  
    75.     cout << "Maximum Row Size per Table: " << dbcon_meta->getMaxRowSize() << " bytes" << endl;  
    76.   
    77.     cout << "/nDatabase schemas: " << endl;  
    78.   
    79.     auto_ptr < ResultSet > rs ( dbcon_meta->getSchemas());  
    80.   
    81.     cout << "/nTotal number of schemas = " << rs->rowsCount() << endl;  
    82.     cout << endl;  
    83.   
    84.     int row = 1;  
    85.   
    86.     while (rs->next()) {  
    87.         cout << "/t" << row << ". " << rs->getString("TABLE_SCHEM") << endl;  
    88.         ++row;  
    89.     } // while  
    90.   
    91.     cout << endl << endl;  
    92. }  
    93.   
    94. /* 获取结果集信息 */  
    95. static void GetResultDataMetaBata(ResultSet *rs)   
    96. {  
    97.   
    98.     if (rs -> rowsCount() == 0)   
    99.     {  
    100.         throw runtime_error("ResultSetMetaData FAILURE - no records in the result set");  
    101.     }  
    102.   
    103.     cout << "ResultSet Metadata" << endl;  
    104.     cout << "------------------" << endl;  
    105.   
    106.     /* The following commented statement won't work with Connector/C++ 1.0.5 and later */  
    107.     //auto_ptr < ResultSetMetaData > res_meta ( rs -> getMetaData() );  
    108.   
    109.     ResultSetMetaData *res_meta = rs -> getMetaData();  
    110.   
    111.     int numcols = res_meta -> getColumnCount();  
    112.     cout << "/nNumber of columns in the result set = " << numcols << endl << endl;  
    113.   
    114.     cout.width(20);  
    115.     cout << "Column Name/Label";  
    116.     cout.width(20);  
    117.     cout << "Column Type";  
    118.     cout.width(20);  
    119.     cout << "Column Size" << endl;  
    120.   
    121.     for (int i = 0; i < numcols; ++i)   
    122.     {  
    123.         cout.width(20);  
    124.         cout << res_meta -> getColumnLabel (i+1);  
    125.         cout.width(20);   
    126.         cout << res_meta -> getColumnTypeName (i+1);  
    127.         cout.width(20);   
    128.         cout << res_meta -> getColumnDisplaySize (i+1) << endl << endl;  
    129.     }  
    130.   
    131.     cout << "/nColumn /"" << res_meta -> getColumnLabel(1);  
    132.     cout << "/" belongs to the Table: /"" << res_meta -> getTableName(1);  
    133.     cout << "/" which belongs to the Schema: /"" << res_meta -> getSchemaName(1) << "/"" << endl << endl;  
    134. }  
    135.   
    136. /* 打印结果集中的数据 */  
    137. static void RetrieveDataAndPrint(ResultSet *rs, int type, int colidx, string colname)   
    138. {  
    139.   
    140.     /* retrieve the row count in the result set */  
    141.     cout << "/nRetrieved " << rs->rowsCount() << " row(s)." << endl;  
    142.   
    143.     cout << "/nCityName" << endl;  
    144.     cout << "--------" << endl;  
    145.   
    146.     /* fetch the data : retrieve all the rows in the result set */  
    147.     while (rs->next())   
    148.     {  
    149.         if (type == NUMOFFSET)   
    150.         {  
    151.             cout << rs -> getString(colidx) << endl;  
    152.         } else if (type == COLNAME)   
    153.         {  
    154.             cout << rs -> getString(colname) << endl;  
    155.         } // if-else  
    156.     } // while  
    157.   
    158.     cout << endl;  
    159. }  
    160.   
    161. void Demo()   
    162. {  
    163.   
    164.     Driver *driver;  
    165.     Connection *con;  
    166.     Statement *stmt;  
    167.     ResultSet *res;  
    168.     PreparedStatement *prep_stmt;  
    169.     Savepoint *savept;  
    170.   
    171.     int updatecount = 0;  
    172.   
    173.     /* initiate url, user, password and database variables */  
    174.     string url(DBHOST);  
    175.     const string user(USER);  
    176.     const string password(PASSWORD);  
    177.     const string database(DATABASE);  
    178.   
    179.     try   
    180.     {  
    181.         driver = get_driver_instance();  
    182.   
    183.         /* create a database connection using the Driver */  
    184.         con = driver -> connect(url, user, password);  
    185.   
    186.         /* alternate syntax using auto_ptr to create the db connection */  
    187.         //auto_ptr  con (driver -> connect(url, user, password));  
    188.   
    189.         /* turn off the autocommit */  
    190.         con -> setAutoCommit(0);  
    191.   
    192.         cout << "/nDatabase connection/'s autocommit mode = " << con -> getAutoCommit() << endl;  
    193.   
    194.         /* select appropriate database schema */  
    195.         con -> setSchema(database);  
    196.   
    197.         /* retrieve and display the database metadata */  
    198.         GetDBMetaData(con);  
    199.   
    200.         /* create a statement object */  
    201.         stmt = con -> createStatement();  
    202.   
    203.         cout << "Executing the Query: /"SELECT * FROM City/" .." << endl;  
    204.   
    205.         /* run a query which returns exactly one result set */  
    206.         res = stmt -> executeQuery ("SELECT * FROM City");  
    207.   
    208.         cout << "Retrieving the result set .." << endl;  
    209.   
    210.         /* retrieve the data from the result set and display on stdout */  
    211.         RetrieveDataAndPrint (res, NUMOFFSET, 1, string("CityName"));  
    212.   
    213.         /* retrieve and display the result set metadata */  
    214.         GetResultDataMetaBata (res);  
    215.   
    216.         cout << "Demonstrating Prepared Statements .. " << endl << endl;  
    217.   
    218.         /* insert couple of rows of data into City table using Prepared Statements */  
    219.         prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");  
    220.   
    221.         cout << "/tInserting /"London, UK/" into the table, City .." << endl;  
    222.   
    223.         prep_stmt -> setString (1, "London, UK");  
    224.         updatecount = prep_stmt -> executeUpdate();  
    225.   
    226.         cout << "/tCreating a save point /"SAVEPT1/" .." << endl;  
    227.         savept = con -> setSavepoint ("SAVEPT1");  
    228.   
    229.         cout << "/tInserting /"Paris, France/" into the table, City .." << endl;  
    230.   
    231.         prep_stmt -> setString (1, "Paris, France");  
    232.         updatecount = prep_stmt -> executeUpdate();  
    233.   
    234.         cout << "/tRolling back until the last save point /"SAVEPT1/" .." << endl;  
    235.         con -> rollback (savept);  
    236.         con -> releaseSavepoint (savept);  
    237.   
    238.         cout << "/tCommitting outstanding updates to the database .." << endl;  
    239.         con -> commit();  
    240.   
    241.         cout << "/nQuerying the City table again .." << endl;  
    242.   
    243.         /* re-use result set object */  
    244.         res = NULL;  
    245.         res = stmt -> executeQuery ("SELECT * FROM City");  
    246.   
    247.         /* retrieve the data from the result set and display on stdout */  
    248.         RetrieveDataAndPrint(res, COLNAME, 1, string ("CityName"));  
    249.   
    250.         cout << "Cleaning up the resources .." << endl;  
    251.   
    252.         /* Clean up */  
    253.         delete res;  
    254.         delete stmt;  
    255.         delete prep_stmt;  
    256.         con -> close();  
    257.         delete con;  
    258.   
    259.     } catch (SQLException &e) {  
    260.         cout << "ERROR: " << e.what();  
    261.         cout << " (MySQL error code: " << e.getErrorCode();  
    262.         cout << ", SQLState: " << e.getSQLState() << ")" << endl;  
    263.   
    264.         if (e.getErrorCode() == 1047) {  
    265.             /* 
    266.             Error: 1047 SQLSTATE: 08S01 (ER_UNKNOWN_COM_ERROR) 
    267.             Message: Unknown command 
    268.             */  
    269.             cout << "/nYour server does not seem to support Prepared Statements at all. ";  
    270.             cout << "Perhaps MYSQL < 4.1?" << endl;  
    271.         }  
    272.   
    273.         return;  
    274.     } catch (std::runtime_error &e) {  
    275.         cout << "ERROR: " << e.what() << endl;  
    276.   
    277.         return;  
    278.     }  
    279.   
    280.     return;  
    281. }  
     
     
    建立数据库连接
        sql::Connection代表到数据库的连接,可以通过sql::Driver来创建。sql::mysql::get_mysql_driver_instance()方法用于获取sql::Driver,通过调用sql::Driver::connect方法来创建sql::Connection对象。(译者注:笔者使用的Connector/C++版本与作者使用的版本不一样,接口方面也有点细微的差别。这里根据笔者使用的最新版本mysql-connector-c++-noinstall-1.0.5-win32来说明。)
        下面是get_mysql_driver_instance与connect这两个方法的签名:
    1.                     /* mysql_driver.h */  
    2. MySQL_Driver *sql::mysql::get_mysql_driver_instance()  
    3.   
    4. /* mysql_driver.h */  
    5. sql::Connection * connect(const std::string& hostName, const std::string& userName, const std::string& password);  
    6. sql::Connection * connect(std::map<std::string, sql::ConnectPropertyVal> & options);  
        Driver类重载了connect方法,一个接收数据库地址的url、用户名和密码的字符串,后一个接收一个map,map中以key/value的形式包含数据库地址、用户名与密码。使用TCP/IP连接到MySql服务器的url字符串的格式如下:"tcp://[hostname[:port]][/schemaname]"。例如:tcp://127.0.0.1:5555/some_scehma。hostname和端口号是可选的,如果省略,默认是127.0.0.1与3306。如果hostname为"localhost",会被自动转换为"127.0.0.1"。schemaname也是可选的,如果连接字符串中没有设置schema,需要在程序中通过Connection::setSchema方法手动设置。
        在unix系统上,可以通过UNIX domain socket连接运行在本地的MySQL服务,连接字符串格式为:"unix://path/to/unix_socket_file",例如:unix:///tmp/mysql.sock.
        在windows系统上,可以以命名管道的方式连接到运行在本地的MySQL数据库,连接字符串格式为:"pipe://path/to/the/pipe"。MySQL服务必须启动允许命名管道连接,可以在启动MySQL服务器的时候,通过--enable-named-pipe命令行选项来启动该功能。如果没有通过--socket=name选项设置命名管道的名称,系统默认使用MySQL。在windows上,管道的名称是区别大小写的。
        下面的代码片断尝试连接到本地的MySQL服务器,通过3306端口,用户名为root,密码是000000,schema为test.
    1.                     sql::mysql::MySQL_Driver *driver = 0;  
    2. sql::Connection *conn = 0;  
    3.   
    4. try   
    5. {  
    6.     driver = sql::mysql::get_mysql_driver_instance();  
    7.     conn = driver->connect("tcp://localhost:3306/test""root""000000");  
    8.       
    9.     cout << "连接成功" << endl;  
    10. }  
    11. catch (...)  
    12. {  
    13.     cout << "连接失败" << endl;  
    14. }  
    15.   
    16. if (conn != 0)  
    17. {  
    18.     delete conn;  
    19. }  

    也可以通过connection的第二个重载方法连接MySQL。ConnectPropertyVal是union类型,在connection.h中定义。
    1.                     sql::mysql::MySQL_Driver *driver = 0;  
    2. sql::Connection *conn = 0;  
    3.   
    4. std::map<std::string, ConnectPropertyVal> connProperties;   
    5. ConnectPropertyVal tmp;   
    6. tmp.str.val = "tcp://127.0.0.1:3306/test";   
    7. connProperties[std::string("hostName")] = tmp;   
    8. tmp.str.val = "root";   
    9. connProperties[std::string("userName")] = tmp;   
    10. tmp.str.val = "000000";   
    11. connProperties[std::string("password")] = tmp;   
    12. try   
    13. {   
    14.     driver = sql::mysql::get_mysql_driver_instance();  
    15.     conn = driver -> connect(connProperties);   
    16.     cout << "连接成功" << endl;  
    17. }   
    18. catch(...)   
    19. {   
    20.     cout << "连接失败" << endl;  
    21. }  
    22.   
    23. if (conn != 0)  
    24. {  
    25.     delete conn;  
    26. }  
        上面的连接字符串可以将协议与路径分开写(译者注:C++会把两个连在一起的字符串合并成一个字符串),如:mp.str.val = "unix://" "/tmp/mysql.sock"
        当建立与服务器之间的连接后,通过Connection::setSessionVariable方法可以设置像sql_mode这样的选项。
    C++细节注意点
        像Connection这样的对象,必须在用完之后,显式的delete,例如:
    1.                 sql::Connection *conn = driver -> connect("tcp://127.0.0.1:3306""root""000000");  
    2. // do something  
    3. delete conn  
        使用使用auto_ptr来维护连接对象的清理, 如:
    1.                 use namespace std;  
    2. use namespace sql;  
    3.   
    4. auto_ptr < Connection > con ( driver -> connect("tcp://127.0.0.1:3306""root""000000") );  
    获取Statement对象
        Statement对象用于向MySQL服务器发送SQL语句。该对象可以通过调用Connection::createStatement方法获得。Statement向MySQL发送一个静态的SQL语句,然后从MySQL获取操作的结果,我们无法向它提供sql参数。如果要向它传递参数,可以使用PreparedStatemenet类。如果相同的SQL语句(只SQL参数不同)要被执行多次,建议使用PreparedStatement类。
        Connection::createStatement的签名如下(关于Connection类所提供的方法列表,可以查看connection.h头文件):
    1. /* connection.h */   
    2. ment* Connection::createStatement();  
        下面的的代码段通过调用Connection对象的createStatemenet来获取一个Statement对象:
    1.                     Connection *conn;  // Connection对象的引用  
    2. Statement *stat;   
    3. Statement stat = conn -> createStatement();   
    执行SQL语句
        在执行SQL语句之前应该通过Connection对象的setSchema方法设置相应的Schema(如果没有在数据库地址URL中指定schema)。
        Statement::executeQuery用于执行一个Select语句,它返回ResultSet对象。Statement::executeUpdate方法主要用于执行INSERT, UPDATE, DELETE语句(executeUpdate可以执行所有的SQL语句,如DDL语句,像创建数据表。),该方法返回受影响记录的条数。
        如果你不清楚要执行的是像select这样的查询语句还是像update/insert/delete这样的操作语句,可以使用execute方法。对于查询语句,execute()返回True,然后通过getResultSet方法获取查询的结果;对于操作语句,它返回False,通过getUpdateCount方法获取受影响记录的数量。
        在一些特殊的情况下,单条SQL语句(如执行存储过程),可能会返回多个结果集 和/或 受影响的记录数量。如果你不想忽略这些结果,通过getResultSet或getUpdateCount方法第一个结果后,再通过getMoreResults()来获取其他的结果集。
        下面是这些方法的签名,可以在statement.h头文件中查阅Statement的完整方法列表。
    1.                 /* connection.h */   
    2. void Connection::setSchema(const std::string& catalog);   
    3. /* statement.h */   
    4. ResultSet* Statement::executeQuery (const std::string& sql);   
    5. int Statement::executeUpdate (const std::string& sql);   
    6. bool Statement::execute (const std::string& sql);   
    7. ResultSet* Statement::getResultSet();   
    8. uint64_t Statement::getUpdateCount();  
        这些方法出错时都会抛出SQLException异常,所以在你的代码中应该使用try...catch语句块来捕获这些异常。
        现在回顾上面那个完全的例子,你会发现获取City表的所有记录是如此的简单:
    1.                 Statement *stmt;  
    2. ResultSet *res;  
    3.   
    4. res = stmt -> executeQuery ("SELECT * FROM City");  
        executeQuery方法返回ResultSet对象,它包含了查询的结果。在以下情况下,executeQuery会抛出SQLException异常:数据库在执行查询时出错;在一个关闭的Statement对象上调用executeQuery;给出的SQL语句返回的不是一个简单的结果集;
        上面的代码可以用Statement::execute()重写:
    1.                 bool retvalue = stmt -> execute ("SELECT * FROM City");  
    2.   
    3. if (retvalue)   
    4. {  
    5.     res = stmt -> getResultSet();  
    6. }   
    7. else   
    8. {  
    9.     ...  
    10. }  
        execute()返回True表示操作的结果是一个ResultSet对象,否则结果是受影响记录的数量或没有结果。当返回True时,通过getResultSet方法获取结果集,在返回False的情况下调用getResultSet方法,将返回NULL。
        当数据库在执行时发生错误或者在一个已关闭的Statement对象上执行execute与getResultSet方法,都会抛出SQLException异常。
        如果要往数据库里添加一条新的记录,可以像下面的例子一样简单的调用executeUpdate方法:
    1. int updateCount = stmt -> executeUpdate ("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");  
        如果executeUpdate执行的是像INSERT, UPDATE或DELETE这样的数据操作语句(DML),它返回受影响的记录的数量;如果执行的是数据定义语句(DDL),它返回0。在数据库操作失败,或者在一个已经关闭的Statement上调用该方法,或者给出的SQL语句是一个查询语句(会返回结果集),该方法会抛出SQLException异常。
        下面的代码使用execute和getUpdateCount方法来生写上面的例子:
    1.                 int updateCount = 0;  
    2. bool retstatus = stat->execute("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");  
    3. if (!retstatus)   
    4. {  
    5.     updateCount = stat->getUpdateCount();  
    6. }   
    7. else   
    8. {  
    9.     ...  
    10. }  
    从ResultData中获取数据

        上面的段落介绍了执行SQL查询的方法:executeQuery和execute,用于获取ResultSet对象。我们可以通过ResultSet访问查询的结果。每一个ResultSet都包含一个游标(cursor),它指向数据集中的当前记录行。ResultSet中排列的记录是有序的(译者注:只能按顺序一条一条获取,不能跳跃式获取)。(但)在同一行中,列值的访问却是随意的:可以通过列的位置或者名称。通过列的名称访问列值让代码更清晰,而通过位置访问列值则更高效。

        列的名称通过SQL语句的AS子名设定,如果SQL语句中没有使用AS子名,列的名称默认为数据表中对应的列名。例如对于"SELECT CityName AS CN FROM City",CN就是结果集中列的名称。
        在ResultSet中的数据,可以通过getXX系列方法来获取,例如:getString(), getInt(),"XX"取决于数据的类型。next()与previous()使游标移到结果集中的下一条或上一条记录。
        Statement执行SQL语句返回ResultSet对象后,ResultSet就变成一个独立的对象,与原先的Statement再也没有联系,即使Statement对象关闭,重新执行其他sql语句,或者获取多个结果集中的下一个。ResultSet将一直有效,除非显式或隐式地将其关闭。
        在撰写本文时,对于Statement对象,MySQL Connector/C++总是返回缓存结果,这些结果在客户端缓存。不管结果集数据量大小,MySQLConnector/C++ Driver总是获取所有的数据。希望以后的版本中,Statement对象能够返回缓存和非缓存的结果集。
        下面是数据获取方法的签名,可以在resultset.h头文件中查看所有ResultSet类支持的方法。
    1.                     /* resultset.h */  
    2. size_t ResultSet::rowsCount() const;  
    3. void ResultSet::close();  
    4.   
    5. bool ResultSet::next();  
    6. bool ResultSet::previous();  
    7. bool ResultSet::last();  
    8. bool ResultSet::first();  
    9.   
    10. void ResultSet::afterLast();  
    11. void ResultSet::beforeFirst();  
    12.   
    13. bool ResultSet::isAfterLast() const;  
    14. bool ResultSet::isBeforeFirst()const;  
    15. bool ResultSet::isClosed() const;  
    16.   
    17. bool ResultSet::isNull(uint32_t columnIndex) const;  
    18. bool ResultSet::isNull(const std::string& columnLabel) const;  
    19. bool ResultSet::wasNull() const;  
    20.   
    21. std::string ResultSet::getString(uint32_t columnIndex) const;  
    22. std::string ResultSet::getString(const std::string& columnLabel) const;  
    23.   
    24. int32_t ResultSet::getInt(uint32_t columnIndex) const;  
    25. int32_t ResultSet::getInt(const std::string& columnLabel) const;  
        在下面的简单示例中,查询语句"SELECT * FROM City"返回的ResultSet中只包含一列:CityName,数据类型为String,对应MySQL中的VARCHAR类型。这个例子通过next方法循环从结果集中获取CityName值,并显示在控制台上:
    1. while (res -> next())  
    2.   
    3. cout << rs -> getString("CityName") << endl;  

        也可以通过位置来获取列值(位置从1开始而非从0开始),下面的代码产生相同的结果:

    1. while (res -> next())   
    2.   
    3.     cout << rs -> getString(1) << endl;  
        如果数据库中该字段的值为NULL,getString将返回一个空的字符串。Result::isNull用于判断指定列在数据库中的值是否为NULL。Result::wasNULL()用于判断最近读取的列的值是否为空。
        下面的例子演示了通过cursor(游标)倒序读取结果集中的数据:
    1.                 /* Move the cursor to the end of the ResultSet object, just after the last row */  
    2. res -> afterLast();  
    3.   
    4. if (!res -> isAfterLast())   
    5. {  
    6.     throw runtime_error("Error: Cursor position should be at the end of the result set after the last row.");  
    7. }  
    8.   
    9. /* fetch the data : retrieve all the rows in the result set */  
    10. while (res -> previous())   
    11. {  
    12.     cout << rs->getString("CityName") << endl;  
    13. }  

        getString方法在以下情况下会抛出SQLException异常:指定列名或位置不存在;数据库在执行操作时失败;在一个关闭的cursor上执行调用该方法。


  • 相关阅读:
    java 新建文件夹保存
    android AppWidgwtProvider学习
    GPS定位
    android ContentProvider学习
    Android 解屏幕锁与点亮屏幕(来电时效果)
    RecyclerView 分页滑动,设置可以滑动下一页,下一个item作为起点方法
    Glide 加载图片 到动态ImageView
    一个例子了解 HandlerThread,Looper,ThreadLocal,Handler,MessageQueue,Message的关系
    android TabLayout 相关
    屏幕相关
  • 原文地址:https://www.cnblogs.com/sunylat/p/6119145.html
Copyright © 2011-2022 走看看