MySQL Connector/C++入門教程(上)


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, ):
  • Database MySQL Server 5.1.24-rc
  • C++ Driver MySQL Connector/C++ 1.0.5
  • MySQL Client Library MySQL Connector/C 6.0
  • Compiler Sun Studio 12 C++ compiler
  • Make CMake 2.6.3
  • Operating System OpenSolaris 2008.11 32-bit
  • CPU / ISA Intel Centrino / x86
  • Hardware Toshiba 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:
[sql]  view plain copy
 
  1. Create Table: CREATE 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演示事務;
  •     獲取結果集和數據庫的元信息;
    例子代碼僅僅用於演示,不建議讀者在實際開發中使用這種樣式的代碼。(譯者注:例子代碼很長,如果看不太明白,沒關系,等閱讀完全文之后再回過頭來看)
[cpp]  view plain copy
 
  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這兩個方法的簽名:
[cpp]  view plain copy
 
  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.
[cpp]  view plain copy
 
  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中定義。
[cpp]  view plain copy
 
  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,例如:
[cpp]  view plain copy
 
  1.                 sql::Connection *conn = driver -> connect("tcp://127.0.0.1:3306", "root", "000000");  
  2. // do something  
  3. delete conn  
    使用使用auto_ptr來維護連接對象的清理, 如:
[cpp]  view plain copy
 
  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頭文件):
[cpp]  view plain copy
 
  1. /* connection.h */   
  2. ment* Connection::createStatement();  
    下面的的代碼段通過調用Connection對象的createStatemenet來獲取一個Statement對象:
[cpp]  view plain copy
 
  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的完整方法列表。
[cpp]  view plain copy
 
  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表的所有記錄是如此的簡單:
[cpp]  view plain copy
 
  1.                 Statement *stmt;  
  2. ResultSet *res;  
  3.   
  4. res = stmt -> executeQuery ("SELECT * FROM City");  
    executeQuery方法返回ResultSet對象,它包含了查詢的結果。在以下情況下,executeQuery會拋出SQLException異常:數據庫在執行查詢時出錯;在一個關閉的Statement對象上調用executeQuery;給出的SQL語句返回的不是一個簡單的結果集;
    上面的代碼可以用Statement::execute()重寫:
[cpp]  view plain copy
 
  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方法:
[cpp]  view plain copy
 
  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方法來生寫上面的例子:
[cpp]  view plain copy
 
  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類支持的方法。
[cpp]  view plain copy
 
  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值,並顯示在控制台上:
[cpp]  view plain copy
 
  1. while (res -> next())  
  2.   
  3. cout << rs -> getString("CityName") << endl;  

    也可以通過位置來獲取列值(位置從1開始而非從0開始),下面的代碼產生相同的結果:

[cpp]  view plain copy
 
  1. while (res -> next())   
  2.   
  3.     cout << rs -> getString(1) << endl;  
    如果數據庫中該字段的值為NULL,getString將返回一個空的字符串。Result::isNull用於判斷指定列在數據庫中的值是否為NULL。Result::wasNULL()用於判斷最近讀取的列的值是否為空。
    下面的例子演示了通過cursor(游標)倒序讀取結果集中的數據:
[cpp]  view plain copy
 
  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上執行調用該方法。

 

 

 

 

附:C++操作mySQL加載到項目

 

C++通過mysql的c api和通過mysql的Connector C++ 1.1.3操作mysql的兩種方式

 

使用vs2013和64位的msql 5.6.16進行操作

image

 

 

項目中使用的數據庫名為booktik

表為book

image

……….

(共有30條記錄,只列出了部分記錄,14-30未列出)

一、通過mysql的C api進行操作

 

1、新建一個空項目

 

2、將D:\Program Files\MySQL\MySQL Server 5.6\include添加到項目的包含目錄中(根據具體路徑而定)

image

 

 

3、將D:\Program Files\MySQL\MySQL Server 5.6\lib添加到項目的庫目錄中(根據具體路徑而定)

image

 

4、添加libmysql.lib至附加依賴項中

image

*3.4步也可以在程序代碼的開始處加上#pragma comment(lib,"D:\\Program Files\\MySQL\\MySQL Server 5.6\\lib\\libmysql.lib") 來導入libmysql.lib)

 

5、如果使用的mysql是64位的,還需要將項目的解決方案平台由win32改成x64

image

 

6、將D:\Program Files\MySQL\MySQL Server 5.6\lib(根據具體路徑而定)下的libmysql.dll復制到項目中去,和.cpp,.h文件位於同一路徑下

 

至此,相關配置全部完成

 

程序代碼

main.cpp

復制代碼
#include <Windows.h>
#include <mysql.h> #include <string> #include <iostream> using namespace std; #pragma comment(lib,"D:\\Program Files\\MySQL\\MySQL Server 5.6\\lib\\libmysql.lib") int main() { const char user[] = "root"; const char pswd[] = "123456"; const char host[] = "localhost"; const char table[] = "booktik"; unsigned int port = 3306; MYSQL myCont; MYSQL_RES *result; MYSQL_ROW sql_row; int res; mysql_init(&myCont); if (mysql_real_connect(&myCont, host, user, pswd, table, port, NULL, 0)) { mysql_query(&myCont, "SET NAMES GBK"); //設置編碼格式 res = mysql_query(&myCont, "select * from book");//查詢 if (!res) { result = mysql_store_result(&myCont); if (result) { while (sql_row = mysql_fetch_row(result))//獲取具體的數據  { cout<<"BOOKNAME:" << sql_row[1] << endl; cout<<" SIZE:" << sql_row[2] << endl; } } } else { cout << "query sql failed!" << endl; } } else { cout << "connect failed!" << endl; } if (result != NULL) mysql_free_result(result); mysql_close(&myCont); system("pause"); return 0; }
復制代碼

 

 

運行結果如下:

image

 

 

二、通過mysql的Connector C++ 1.1.3進行操作

MySQL C++ Driver的實現基於JDBC規范
MySQL Connector/C++是由Sun Microsystems開發的MySQL連接器。它提供了基於OO的編程接口與數據庫驅動來操作MySQL服務器。
與許多其他現存的C++接口實現不同,Connector/C++遵循了JDBC規范。也就是說,Connector/C++ Driver的API主要是基於Java語言的JDBC接口。JDBC是java語言與各種數據庫連接的標准工業接口。
Connector/C++實現了大部分JDBC規范。如果C++程序的開發者很熟悉JDBC編程,將很快的入門。

 

MySQL Connector/C++需要安裝配置boost庫,boost庫安裝編譯在這里不進行闡述

 

1、新建一個空項目

 

2、將D:\Program Files\MySQL\Connector C++ 1.1.3\include添加到項目的包含目錄中(根據具體路徑而定)

image

 

3、將D:\boost\boost_1_55_0添加到項目的包含目錄中(根據具體路徑而定)

image

 

 

4、將D:\Program Files\MySQL\Connector C++ 1.1.3\lib\opt添加到項目的庫目錄中(根據具體路徑而定)

image

 

5、添加mysqlcppconn.lib至附加依賴項中

image

 

6、如果使用的mysql是64位的,還需要將項目的解決方案平台由win32改成x64

image

 

7、將D:\Program Files\MySQL\Connector C++ 1.1.3\lib\opt(根據具體路徑而定)下的mysqlcppconn.dll復制到項目中去,和.cpp,.h文件位於同一路徑下

      將D:\Program Files\MySQL\MySQL Server 5.6\lib(根據具體路徑而定)下的libmysql.dll復制到項目中去,和.cpp,.h文件位於同一路徑下

 

 

至此,相關配置全部完成

 

程序代碼

main.cpp

復制代碼
#include <iostream>
#include <map> #include <string> #include <memory> #include "mysql_driver.h" #include "mysql_connection.h" #include "cppconn/driver.h" #include "cppconn/statement.h" #include "cppconn/prepared_statement.h" #include "cppconn/metadata.h" #include "cppconn/exception.h" using namespace std; using namespace sql; int main() { sql::mysql::MySQL_Driver *driver = 0; sql::Connection *conn = 0; try { driver = sql::mysql::get_mysql_driver_instance(); conn = driver->connect("tcp://localhost:3306/booktik", "root", "123456"); cout << "連接成功" << endl; } catch (...) { cout << "連接失敗" << endl; } sql::Statement* stat = conn->createStatement(); stat->execute("set names 'gbk'"); ResultSet *res; res = stat->executeQuery("SELECT * FROM BOOK"); while (res->next()) { cout << "BOOKNAME:" << res->getString("bookname") << endl; cout << " SIZE:" << res->getString("size") << endl; } if (conn != 0) { delete conn; } system("pause"); }
復制代碼

 

運行結果

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM