MySQL Connector/C++(一)


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

如果我翻譯錯了或者翻譯的不好,歡迎指正~


Developing Database Applications Using MySQL Connector/C++

這個教程會教你搭建安裝MySQL Connector/C++ driver的要點和步驟,以一個簡單的連接MySQL的例子,從MySQL中獲取數據並對其進行插入(數據)操作。因為重點在於從C++程序連接數據庫,所以本文檔假設MySQL已經運行並且能從客戶端訪問。

本文是面向那些初次接觸MySQL Connector/C++的程序開發者的教程,並不是講述C++編程和MySQL數據庫的。

構建和運行本教程的例子需要下列工具和技術來編譯:

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

CONTENTS

MySQL C++ Driver 基於JDBC 4.0標准實現

MySQL Connector/C++是最新發布的MySQL連接器,由 Sun Microsystems開發。MySQL connector為C++提供面向對象的編程接口(API)和連接MySQL Server的數據庫驅動器

與現存的driver不同,Connector/C++是JDBC API在C++中的實現。換句話說,Connector/C++ driver的接口主要是基於Java語言的JDBC API。Java數據庫連接(JDBC)是Java連接各種數據庫的業界標准。Connector/C++實現了JDBC 4.0的大部分規范。熟悉JDBC編程的C++程序開發者可以提高程序開發的效率。

MySQL Connecotr/C++實現了以下類:

  • Driver
  • Connection
  • Statement
  • PreparedStatement
  • ResultSet
  • Savepoint
  • DatabaseMetaData
  • ResultSetMetaData
  • ParameterMetaData

Connecotr/C++ driver可用於連接MySQL5.1以及后續版本。

在MySQL Connector/C++出現之前,C++程序員需要使用非標准的、過程化的MySQL C API或MySQL++ API連接MySQL,MySQL Connector/C++是MySQL C API的C++封裝

安裝MySQL Connector/C++

從二進制程序安裝

從1.0.4版本開始,Connector/C++可用於Solaris, Linux, Windows, FreeBSD, Mac OS X, HP-UX and AIX平台。MSI安裝程序和二進制ZIP文件並不需要安裝程序可用於Windows,GNU TAR的壓縮文檔(tar.gz)可用於其他的平台。你可以從“Connector/C++ download“下載預編譯的二進制文件。

在Windows和其他平台下二進制包的安裝是非常簡單的-簡單的解壓縮文檔到指定位置安裝Connector/C++ driver。靜態鏈接和動態鏈接的Connector/C++ driver可以在安裝目錄下找到lib目錄。如果您打算使用動態鏈接版本的MySQL連接器/ C + +,要確保運行時鏈接程序可以找到MySQL客戶端庫。請查閱你操作系統文檔,修改和擴展庫的搜索路徑。如果你無法修改庫的搜索路徑,那么復制你的程序、MySQL Connector/C++ driver和MySQL客戶端庫到相同的目錄。這種方法在大多數平台都可行,編譯器到其他地方都所必須動態庫之前會先搜索原始目錄(當前目錄)。

從源碼安裝

如果需要從源碼編譯安裝,請查看“Installing MySQL Connector/C++ from Source“頁面的詳細介紹。

運行時依賴

略(英語水平有限,這一段翻譯的不通暢,就不寫了,於閱讀本教程沒什么影響) 

使用IDE開發C++程序

如果你正在尋找一個集成開發環境(IDE)來開發C/C++程序,可以考慮使用開源免費的NetBeans平台。NetBeans C/C++開發包讓程序員可以使用他們指定的編譯器和工具來搭配NetBeans IDE,構建Solaris,Linux,Windows和Mac OS X的原生應用。C/C++開發包使編輯器具有語言識別功能,可以識別C/C++語言,並且提供項目模板,一個動態類瀏覽器,支持Makefile和m(不知道是什么)調試器功能。可以通過模塊和插件來擴展C/C++開發包的基礎功能。

MySQL Connector/C++: How to Build a Client using NetBeans 6.5 (for Dummies) 教程介紹了如何使用NetBeans IDE來構建基於Connector/C++的客戶端程序。除了上面的教程,在NetBeans.org網站上的Installing and Configuring C/C++ Support (安裝和配置支持)教程可以幫助你安裝和配置C/C++開發包,並且”Getting Started With the NetBeans C/C++ Development Pack“提供了使用NetBeans C/C++開發包參與開發一個C/C++程序的基本步驟

為實例代碼創建City表和test數據庫

本教程中的簡單示例代碼嘗試獲取MySQL中的test數據庫中的City表的數據。此表的結構和數據已經使用mysql客戶端顯示在下面。MySQL服務運行在3306默認端口。

# mysql -u root -p
Enter password: admin
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.1.24-rc-standard Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> USE test;
Database changed

mysql> DESCRIBE City;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| CityName | varchar(30) | YES  |       | NULL  |       |
+----------+-------------+------+-----+---------+-------+
1 row in set (0.07 sec)

mysql> SHOW CREATE TABLE City\G
*************************** 1. row ***************************
       Table: City
Create Table: CREATE TABLE `City` (
  `CityName` varchar(30) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=ascii
1 row in set (0.00 sec)

mysql> SELECT * FROM City;

+--------------------+
| CityName           |
+--------------------+
| Hyderabad, India   |
| San Francisco, USA |
| Sydney, Australia  |
+--------------------+
3 rows in set (0.17 sec)

本教程的所有例子的運行結果都是使用bash shell顯示

使用Connector/C++測試與數據庫連接

下面的C++示例代碼簡單演示如何使用MySQL Connector/C++連接本機的MySQL服務。實例代碼使用Connector/C++提供的類似於JDBC的API連接到MySQL中的test數據庫,實行一條查詢語句,從City表中獲取所有行數據,從結果集中提取數據並顯示在標准輸出上,使用" Prepared Statements" 插入幾行數據到City表中。

示例代碼僅僅供參考。不建議讀者使用特定的編碼風格。為了簡單起見,實例代碼假設用戶提供了合法的輸入,所以下面例子中並沒有明確的錯誤檢測代碼。

# cat MySQLConnectorC++Client.cpp

/* Standard C++ headers */
#include <iostream>
#include <sstream>
#include <memory>
#include <string>
#include <stdexcept>

/* MySQL Connector/C++ specific headers */
#include <driver.h>
#include <connection.h>
#include <statement.h>
#include <prepared_statement.h>
#include <resultset.h>
#include <metadata.h>
#include <resultset_metadata.h>
#include <exception.h>
#include <warning.h>
    
#define DBHOST "tcp://127.0.0.1:3306"
#define USER "root"
#define PASSWORD "admin"
#define DATABASE "test"

#define NUMOFFSET 100
#define COLNAME 200

using namespace std;
using namespace sql;

static void retrieve_data_and_print (ResultSet *rs, int type, int colidx, string colname) {

    /* retrieve the row count in the result set */
    cout << "\nRetrieved " << rs -> rowsCount() << " row(s)." << endl;

    cout << "\nCityName" << endl;
    cout << "--------" << endl;

    /* fetch the data : retrieve all the rows in the result set */
    while (rs->next()) {
        if (type == NUMOFFSET) {
                       cout << rs -> getString(colidx) << endl;
        } else if (type == COLNAME) {
                       cout << rs -> getString(colname) << endl;
        } // if-else
    } // while

    cout << endl;

} // retrieve_data_and_print()

static void retrieve_dbmetadata_and_print (Connection *dbcon) {

    if (dbcon -> isClosed()) {
        throw runtime_error("DatabaseMetaData FAILURE - database connection closed");
    }

    cout << "\nDatabase Metadata" << endl;
    cout << "-----------------" << endl;

    cout << boolalpha;

    /* The following commented statement won't work with Connector/C++ 1.0.5 and later */
    //auto_ptr < DatabaseMetaData > dbcon_meta (dbcon -> getMetaData());

    DatabaseMetaData *dbcon_meta = dbcon -> getMetaData();

    cout << "Database Product Name: " << dbcon_meta -> getDatabaseProductName() << endl;
    cout << "Database Product Version: " << dbcon_meta -> getDatabaseProductVersion() << endl;
    cout << "Database User Name: " << dbcon_meta -> getUserName() << endl << endl;

    cout << "Driver name: " << dbcon_meta -> getDriverName() << endl;
    cout << "Driver version: " << dbcon_meta -> getDriverVersion() << endl << endl;

    cout << "Database in Read-Only Mode?: " << dbcon_meta -> isReadOnly() << endl;
    cout << "Supports Transactions?: " << dbcon_meta -> supportsTransactions() << endl;
    cout << "Supports DML Transactions only?: " << dbcon_meta -> supportsDataManipulationTransactionsOnly() << endl;
    cout << "Supports Batch Updates?: " << dbcon_meta -> supportsBatchUpdates() << endl;
    cout << "Supports Outer Joins?: " << dbcon_meta -> supportsOuterJoins() << endl;
    cout << "Supports Multiple Transactions?: " << dbcon_meta -> supportsMultipleTransactions() << endl;
    cout << "Supports Named Parameters?: " << dbcon_meta -> supportsNamedParameters() << endl;
    cout << "Supports Statement Pooling?: " << dbcon_meta -> supportsStatementPooling() << endl;
    cout << "Supports Stored Procedures?: " << dbcon_meta -> supportsStoredProcedures() << endl;
    cout << "Supports Union?: " << dbcon_meta -> supportsUnion() << endl << endl;

    cout << "Maximum Connections: " << dbcon_meta -> getMaxConnections() << endl;
    cout << "Maximum Columns per Table: " << dbcon_meta -> getMaxColumnsInTable() << endl;
    cout << "Maximum Columns per Index: " << dbcon_meta -> getMaxColumnsInIndex() << endl;
    cout << "Maximum Row Size per Table: " << dbcon_meta -> getMaxRowSize() << " bytes" << endl;

    cout << "\nDatabase schemas: " << endl;

    auto_ptr < ResultSet > rs ( dbcon_meta -> getSchemas());

    cout << "\nTotal number of schemas = " << rs -> rowsCount() << endl;
    cout << endl;

    int row = 1;

    while (rs -> next()) {
        cout << "\t" << row << ". " << rs -> getString("TABLE_SCHEM") << endl;
        ++row;
    } // while

    cout << endl << endl;

} // retrieve_dbmetadata_and_print()

static void retrieve_rsmetadata_and_print (ResultSet *rs) {

    if (rs -> rowsCount() == 0) {
        throw runtime_error("ResultSetMetaData FAILURE - no records in the result set");
    }

    cout << "ResultSet Metadata" << endl;
    cout << "------------------" << endl;

    /* The following commented statement won't work with Connector/C++ 1.0.5 and later */
    //auto_ptr < ResultSetMetaData > res_meta ( rs -> getMetaData() );

    ResultSetMetaData *res_meta = rs -> getMetaData();

    int numcols = res_meta -> getColumnCount();
    cout << "\nNumber of columns in the result set = " << numcols << endl << endl;

    cout.width(20);
    cout << "Column Name/Label";
    cout.width(20);
    cout << "Column Type";
    cout.width(20);
    cout << "Column Size" << endl;

    for (int i = 0; i < numcols; ++i) {
        cout.width(20);
        cout << res_meta -> getColumnLabel (i+1);
        cout.width(20); 
        cout << res_meta -> getColumnTypeName (i+1);
        cout.width(20); 
        cout << res_meta -> getColumnDisplaySize (i+1) << endl << endl;
    }

    cout << "\nColumn \"" << res_meta -> getColumnLabel(1);
    cout << "\" belongs to the Table: \"" << res_meta -> getTableName(1);
    cout << "\" which belongs to the Schema: \"" << res_meta -> getSchemaName(1) << "\"" << endl << endl;

} // retrieve_rsmetadata_and_print()


int main(int argc, const char *argv[]) {

    Driver *driver;
    Connection *con;
    Statement *stmt;
    ResultSet *res;
    PreparedStatement *prep_stmt;
    Savepoint *savept;

    int updatecount = 0;

    /* initiate url, user, password and database variables */
    string url(argc >= 2 ? argv[1] : DBHOST);
    const string user(argc >= 3 ? argv[2] : USER);
    const string password(argc >= 4 ? argv[3] : PASSWORD);
    const string database(argc >= 5 ? argv[4] : DATABASE);

    try {
        driver = get_driver_instance();
        
        /* create a database connection using the Driver */
        con = driver -> connect(url, user, password);

        /* alternate syntax using auto_ptr to create the db connection */
        //auto_ptr  con (driver -> connect(url, user, password));

        /* turn off the autocommit */
        con -> setAutoCommit(0);

        cout << "\nDatabase connection\'s autocommit mode = " << con -> getAutoCommit() << endl;

        /* select appropriate database schema */
        con -> setSchema(database);

        /* retrieve and display the database metadata */
        retrieve_dbmetadata_and_print (con);

        /* create a statement object */
        stmt = con -> createStatement();

        cout << "Executing the Query: \"SELECT * FROM City\" .." << endl;

        /* run a query which returns exactly one result set */
        res = stmt -> executeQuery ("SELECT * FROM City");

        cout << "Retrieving the result set .." << endl;

        /* retrieve the data from the result set and display on stdout */
        retrieve_data_and_print (res, NUMOFFSET, 1, string("CityName"));

        /* retrieve and display the result set metadata */
        retrieve_rsmetadata_and_print (res);

        cout << "Demonstrating Prepared Statements .. " << endl << endl;

        /* insert couple of rows of data into City table using Prepared Statements */
        prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");

        cout << "\tInserting \"London, UK\" into the table, City .." << endl;

        prep_stmt -> setString (1, "London, UK");
        updatecount = prep_stmt -> executeUpdate();

        cout << "\tCreating a save point \"SAVEPT1\" .." << endl;
        savept = con -> setSavepoint ("SAVEPT1");

        cout << "\tInserting \"Paris, France\" into the table, City .." << endl;

        prep_stmt -> setString (1, "Paris, France");
        updatecount = prep_stmt -> executeUpdate();

        cout << "\tRolling back until the last save point \"SAVEPT1\" .." << endl;
        con -> rollback (savept);
        con -> releaseSavepoint (savept);

        cout << "\tCommitting outstanding updates to the database .." << endl;
        con -> commit();

        cout << "\nQuerying the City table again .." << endl;

        /* re-use result set object */
        res = NULL;
        res = stmt -> executeQuery ("SELECT * FROM City");

        /* retrieve the data from the result set and display on stdout */
        retrieve_data_and_print (res, COLNAME, 1, string ("CityName"));

        cout << "Cleaning up the resources .." << endl;

        /* Clean up */
        delete res;
        delete stmt;
        delete prep_stmt;
        con -> close();
        delete con;

    } catch (SQLException &e) {
        cout << "ERROR: SQLException in " << __FILE__;
        cout << " (" << __func__<< ") on line " << __LINE__ << endl;
        cout << "ERROR: " << e.what();
        cout << " (MySQL error code: " << e.getErrorCode();
        cout << ", SQLState: " << e.getSQLState() << ")" << endl;

        if (e.getErrorCode() == 1047) {
            /*
            Error: 1047 SQLSTATE: 08S01 (ER_UNKNOWN_COM_ERROR)
            Message: Unknown command
            */
            cout << "\nYour server does not seem to support Prepared Statements at all. ";
            cout << "Perhaps MYSQL < 4.1?" << endl;
        }

        return EXIT_FAILURE;
    } catch (std::runtime_error &e) {

        cout << "ERROR: runtime_error in " << __FILE__;
        cout << " (" << __func__ << ") on line " << __LINE__ << endl;
        cout << "ERROR: " << e.what() << endl;

        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
} // main()
# CC -V
CC: Sun C++ 5.9 SunOS_i386 Patch 124864-09 2008/12/16

# CC -o mysqlconnectorc++client -g0 -xO4 -features=extensions -I/opt/coolstack/mysql_32bit/include/mysql \
     -I/export/expts/MySQLConnectorC++/include/cppconn -L/opt/coolstack/mysql_32bit/lib/mysql \
     -L/export/expts/MySQLConnectorC++/lib -lmysqlclient_r -lmysqlcppconn MySQLConnectorC++Client.cpp

# export LD_LIBRARY_PATH=/opt/coolstack/mysql_32bit/lib/mysql:/export/expts/ConnectorC++/lib/:$LD_LIBRARY_PATH

# ./mysqlconnectorc++client localhost root admin test

Database connection's autocommit mode = 0

Database Metadata
-----------------
Database Product Name: MySQL
Database Product Version: 5.1.24-rc-standard
Database User Name: root@localhost

Driver name: MySQL Connector/C++
Driver version: 1.0.5

Database in Read-Only Mode?: false
Supports Transactions?: true
Supports DML Transactions only?: false
Supports Batch Updates?: true
Supports Outer Joins?: true
Supports Multiple Transactions?: true
Supports Named Parameters?: false
Supports Statement Pooling?: false
Supports Stored Procedures?: true
Supports Union?: true

Maximum Connections: 151
Maximum Columns per Table: 512
Maximum Columns per Index: 16
Maximum Row Size per Table: 2147483639 bytes

Database schemas: 

Total number of schemas = 4

    1. information_schema
    2. ISVe
    3. mysql
    4. test


Executing the Query: "SELECT * FROM City" ..
Retrieving the result set ..

Retrieved 3 row(s).

CityName
--------
Hyderabad, India
San Francisco, USA
Sydney, Australia

ResultSet Metadata
------------------

Number of columns in the result set = 1

   Column Name/Label         Column Type         Column Size
            CityName             VARCHAR                  30


Column "CityName" belongs to the Table: "City" which belongs to the Schema: "test"

Demonstrating Prepared Statements .. 

    Inserting "London, UK" into the table, City ..
    Creating a save point "SAVEPT1" ..
    Inserting "Paris, France" into the table, City ..
    Rolling back until the last save point "SAVEPT1" ..
    Committing outstanding updates to the database ..

Querying the City table again ..

Retrieved 4 row(s).

CityName
--------
Hyderabad, India
San Francisco, USA
Sydney, Australia
London, UK

Cleaning up the resources ..

 

上述示例代碼的一些重要步驟說明如下:

建立一個連接到MySQL Server

通過 sql::Driver對象實例的sql::Connection成員與MySQL建立連接,sql::Driver對象通過sql::Driver::get_driver_instance函數的返回值設定,而sql::Driver::connect函數返回一個sql::Connection對象。
 
注:

上一段使用的<namespace>::<classname>::<methodname>符號為完全限定的函數名。例如,在sql::Driver::get_driver_instance()中,sql是名稱空間,Driver是類名以及get_driver_instance()是函數名。在C++程序中如此使用Connector/C++,你可以在代碼頭中使用"using namespace sql;"指令,這樣每次使用相應聲明的時候就不需要用“sql::”前綴。所以本教程其余部分使用“sql”名稱空間中的函數或其他成員時,都省略了“sql”名稱空間的前綴,這樣會更簡單清晰。

下面是Driver::get_driver_instance和Driver::connect函數的簽名。你可以通過查看安裝程序中的driver.h頭文件獲取完整的函數列表。

 

/* driver.h */
Driver* Driver::get_driver_instance()

Connection* Driver::connect(const std::string& URL, const std::string& userName, const std::string& password)
Connection* Driver::connect(std::map<std::string, ConnectPropertyVal> properties)

connect在Driver類中是一個重載函數。這里有兩個重載版本。在第一個版本中,connect接受一個數據庫連接URL以及數據庫的用戶名和密碼。第二個版本中,connector接受一個std::map對象,此對象是包含了數據庫連接的URL,用戶名和密碼的鍵值對。

你可以在連接URL中指定TCP/IP,“tcp://[hostname[:port]][/schemaname]"的形式連接到MySQL。例如:tcp://127.0.0.1:5555/some_schema. hostname和port是可選的,默認使用127.0.0.1和3306.運行時,"localhost"會自動轉換成127.0.0.1。是否指定schema name 也是可選的,如果沒設置,則必須使用Connector::setSchema函數來設置schema。

如果你要使用UNIX域的套接口來連接本機的MySQL Server,需在數據庫連接URL中指定"unix://path/to/unix_socket_file"。例如:unix://tmp/mysql.sock. 在Windwos中,你可以使用命名管道來連接MySQL Server,需要在數據庫連接URL中指定字符串"pipe://path/to/the/pipe"。要啟用管道支持,必須在啟動MySQL時加上--enable-named-pipe選項。如果你沒有使用--socket=name選項指定管道名稱,那么會默認創建名為MySQL的命名管道。在Microsoft Windows中,name不區分大小寫。

下列代碼嘗試連接到本地的MySQL Server默認端口:3306,使用root用戶名,密碼為admin,schema名稱為test.

using namespace sql;

Driver *driver;
Connection *con;

try {

    driver = get_driver_instance();
    con = driver -> connect("tcp://127.0.0.1:3306/test", "root", "admin");

} catch (..) {
    ..
}

 

connect也可以像下面這樣使用,調用第二個重載版本。ConnectPropertyVal是一個枚舉類型,定義在connection.h頭文件中。編譯此段代碼需要包含map頭文件。

..
std::map conn_properties;
ConnectPropertyVal tmp;

tmp.str.val = "unix:///tmp/mysql.sock";
conn_properties [std::string("hostName")] = tmp;

tmp.str.val = "root";
conn_properties [std::string("userName")] = tmp;

tmp.str.val = "admin";
conn_properties [std::string("password")] = tmp;

try {

    driver = get_driver_instance();
    con = driver -> connect(conn_properties);

} catch(..) {
    ..
}

 

如果你更喜歡使用指定UNIX套接口協議,可以重寫上述代碼的數據庫連接URL,如下所示:

            tmp.str.val = "unix://" "/tmp/mysql.sock";

一旦建立了連接,你就可以使用Connection::setSessionVariable函數來設置變量,如sql_mode。

C++ Specific Note

"sql::Connection *con = driver -> connect("tcp://127.0.0.1:3306", "root", "admin");”語句可以使用auto_ptr來重寫:

std::auto_ptr < sql::Connection > con ( driver -> connect("tcp://127.0.0.1:3306", "root", "admin") );

				= OR =

use namespace std;
use namespace sql;

auto_ptr < Connection > con ( driver -> connect("tcp://127.0.0.1:3306", "root", "admin") );

C++標准模板類auto_ptr幫助開發者管理動態內存分配,防止在意想不到的情況下發生內存泄漏,比如在某些情況下,正常的清理(內存)代碼被跳過了。auto_ptr對象與指針具有相同的語義,但是auto_ptr對象超出作用域時將會自動釋放掉動態分配的內存。也就是說,使用auto_ptr時,你不必顯式使用delete操作符來釋放內存,delete con;

要使用auto_ptr類,你需要包含<memory>這個頭文件,並且auto_ptr<type>是定義在std名稱空間內的,所以也要通過std名稱空間來訪問。type可以是指向任意類型/對象的指針。

至於采用auto_ptr智能指針機制,還是傳統機制來動態管理內存,由讀者自己決定。

獲取一個Statement對象

當調用Connection::createStatement函數時,返回一個Statement對象,它可以用來向數據庫服務器發送SQL語句。一般情況下,Statement對象執行的是不帶參數的SQL語句。換句話說,一個Statement對象用於執行靜態SQL語句,並返回其執行結果。如果需要執行多次不同輸入的SQL語句,可以考慮使用Prepared Statements對象。

Connection::createStatement的函數簽名(原型)如下所示,完整的Connection的接口函數列表,請閱看你的Connector/C++安裝文件中的connection.h頭文件

/* connection.h */
Statement* Connection::createStatement();

下面代碼片段調用Connection對象中的createStatement函數,獲得一個Statement類型的對象。

Connection *con;
Statement *stmt;

Statement stmt = con -> createStatement();

這個例子中,con是Connection類型的引用(指針)

執行SQL語句

在執行SQL語句前,你需要先連接數據庫,並且選擇相應的數據庫架構。通過調用Connection對象中的setSchema函數來選擇需要連接的架構,setSchema函數的參數是架構的名稱。

以SQL語句作為(executeQuery的)參數,調用Statement::executeQuery函數執行查詢語句。executeQuery()返回一個ResultSet對象。Statement::executeUpdate函數可用於執行特定的SQL語句,如INSERT,UPDATE,DELETE。或者是不返回任何結果的SQL語句,如SQL DDL語句。與excuteQuery()不同,excuteUpdate函數不返回ResultSet對象。相反,它返回INSERT,UPDATE,DELETE操作后受影響的行數。

如果你事先並不知道SQL語句是SELECT還是INSERT,UPDATE或DELETE,你可以使用execute函數。當SQL語句是SELECT操作時,execute()返回true,當SQL語句是INSERT,UPDATE,DELETE操作時,execute()返回false。如果語句是SELECT查詢操作,你可以調用Statement實例中的getResultSet函數獲取查詢結果集。如果語句是INSERT,UPDATE,DELETE操作,你可以調用getUpdateCount()獲取受影響的行數。

在少數情況下,一條SQL語句可能返回多個結果和/或更新行數。一般情況下,你可以忽略這一點。除非你執行一個存儲過程,你知道可能會返回多個結果;或者動態執行未知的SQL語句。使用getResultSet或getUpdateCount函數可以獲取結果,getMoreResults()可以檢查是否還有其他結果集合。

相應的函數簽名如下所示。完整的Connection和Statement的接口函數列表,請查看connection.h頭文件。

/* connection.h */
void Connection::setSchema(const std::string& catalog);

/* statement.h */
ResultSet* Statement::executeQuery (const std::string& sql);
int Statement::executeUpdate (const std::string& sql);
bool Statement::execute (const std::string& sql);

ResultSet* Statement::getResultSet();
uint64_t Statement::getUpdateCount();

上述所有函數都可能拋出SQLException(SQL異常),所以在代碼中必須確保catch(捕捉)到這些異常。為了簡單起見,在實例代碼中並沒有使用try...catch塊(指retrieve_data_and_print函數中)。如果你重新查看完整的實例代碼,你會發現其目的只是獲取test數據庫中City表的所有行數據。於是,使用executeQuery()的示例代碼如下所示:

Statement *stmt;
ResultSet *res;

res = stmt -> executeQuery ("SELECT * FROM City");

executeQuery函數返回一個ResultSet對象,它包含了給定查詢語句所生成的結果數據。當發生數據庫訪問錯誤,或調用已關閉的Statement對象,或給定的SQL語句產生的數據超過單一ResultSet對象,executeQuery將拋出SQLException。

另外,上述代碼段可以用Statement::execute()重寫,如下所示:

bool retvalue = stmt -> execute ("SELECT * FROM City");

if (retvalue) {
    res = stmt -> getResultSet();
} else {
    ...
}

如果查詢結果是一個ResultSet對象(SELECT操作),execute函數返回true,如果查詢結果是更新的行數(UPDATE,INSERT,DELETE)或者無結果,execute函數返回false。如果execute()返回true,使用getResultSet函數可以獲取結果集。如果查詢結果是更新的行數或者沒有結果,getResultSet返回NULL。

execute和getResultSet這兩個函數在發生數據庫訪問錯誤或在已經關閉的Statement上調用時,都會拋出SQLException.如果你需要插入新記錄到數據庫,你可以使用executeUpdate函數,如下所示:

int updateCount = stmt -> executeUpdate ("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");

executeUpdate執行給定的SQL數據操作語言(DML)語句,例如INSERT,UPDATE,DELETE;或不返回任何數據的SQL語句,例如DDL語句。執行INSERT,UPDATE,DELETE操作,executeUpdate()返回受影響的行數,如果SQL語句不返回任何數據,executeUpdate()返回0值。

當發生數據庫訪問錯誤或在已關閉的Statement對象上調用executeUpdate(),或者是SQL語句產生一個結果集對象,executeUpdate都會拋出SQLException.

另一方面,使用execute和getUpdateCount函數可以重寫上述代碼段,如下所示:

int updateCount = 0;

bool retstatus = stmt -> execute ("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");

if (!retstatus) {
    updateCount = stmt -> getUpdateCount();
} else {
    ...
}

如果SQL語句的執行的返回結果是受(更新)影響的行數,execute函數返回false,此時可以使用getUpdateCount函數獲得受影響行數值。如果SQL語句執行結果返回一個ResultSet對象或者更多結果,getUpdateCount()返回-1.

在已關閉的Statement對象上調用execute和getUpdateCount或出現數據庫訪問錯誤的情況下都會拋出SQLException.

從結果集中獲取數據

前面的段落解析到,executeQuery和execute兩個函數執行SQL查詢語句,前者返回一個ResultSet實例。你可以使用ResultSet對象訪問SQL查詢語句從數據庫返回的數據。沒一個ResultSet維護一個游標(指針),它指向其當前數據行。利用游標可以順序讀取數據集中的行。在一行中,列的值可以以任意順序訪問。你可以根據列的位置(offset)或它們的列名/標簽來訪問,后者具有一定的容錯性,尤其是在表的架構發生改變后。使用列標簽或列名會使代碼更加清晰。另一方面,通過列的索引或位置訪問,可以提高性能。

列標簽就是SQL“AS”子句中指定的標簽名。如果SQL“AS”子句沒有指定標簽,那么標簽名就是列名。例如在“SELECT CityName AS CN FROM City”中,CN就是列標簽名。

存儲在ResultSet中的數據可以通過getXXX函數來獲取,例如getString() 或 getInt(),根據數據類型的不同使用相應的函數獲取數據。使用使用ResulSet的next和previous函數移動游標指向下一行數據。

ResultSet會一直保持打開,即使生成他的Statement對象關閉了,重新執行可以獲取多個結果集隊列中的下一個結果。一旦Statement生成了結果集,該ResultSet會一直有效,直到顯示或隱式的關閉它(才會失效),不論產生它的Statement對象狀態如何,都不會影響到已經產生的ResultSet。

截至撰寫這篇文章時,MySQL Connector/C++的Statement對象返回一個結果緩沖區。緩沖區緩存在客戶端上。不論結果集多大,driver都會獲取所有的數據。connector未來的版本Statement對象有望返回緩沖和非緩沖結果集。

相關的函數簽名如下所示。ResultSet支持的接口完整列表請查看resultset.h頭文件。

/* resultset.h */
size_t ResultSet::rowsCount() const;
void ResultSet::close();

bool ResultSet::next();
bool ResultSet::previous();
bool ResultSet::last();
bool ResultSet::first();

void ResultSet::afterLast();
void ResultSet::beforeFirst();

bool ResultSet::isAfterLast() const;
bool ResultSet::isBeforeFirst()const;
bool ResultSet::isClosed() const;

bool ResultSet::isNull(uint32_t columnIndex) const;
bool ResultSet::isNull(const std::string& columnLabel) const;
bool ResultSet::wasNull() const;

std::string ResultSet::getString(uint32_t columnIndex) const;
std::string ResultSet::getString(const std::string& columnLabel) const;

int32_t ResultSet::getInt(uint32_t columnIndex) const;
int32_t ResultSet::getInt(const std::string& columnLabel) const;

在實例代碼中,語句“SELECT * FROM City”返回的結果集只有一列CityName數據,數據類型為String,在MySQL中為VARCHAR類型

下列代碼片段遍歷ResultSet對象res,通過准確的列名獲取每一行數據,並顯示在標准輸出上。

while (res -> next()) {
    cout << rs -> getString("CityName") << endl;
}

因為也可以通過列的索引訪問,所以下列代碼片段也能得到相同的效果:

while (res -> next()) {
        cout << rs -> getString(1) << endl;
}

getString()的整形參數引用查詢語句中指定的列,列號從1開始。(譯者注:比如“SELECT COLUMN1 COLUMN2 COLUMN3 FROM TABLE”返回的結果中,第一列就是COLUMN1,第二列為COLUMN2,第三列為COLUMN3,通過getString(1)、getString(2)、getString(3)獲取對應數據)

這兩個版本的getString()都返回列值。如果列值為空(NULL),則返回值為空字符串(string對象)。你可以通過ResultSet::isNull函數,以列索引或列名/列標簽作為參數,檢查相應的列值是否為SQL NULL。通過ResultSet::wasNull()函數可以確定讀取的最后一列的值是否為SQL NULL,此函數沒有參數。

下列實例演示如何反向遍歷結果集中的數據:

/* Move the cursor to the end of the ResultSet object, just after the last row */
res -> afterLast();

if (!res -> isAfterLast()) {
    throw runtime_error("Error: Cursor position should be at the end of the result set after the last row.");
}

/* fetch the data : retrieve all the rows in the result set */
while (res -> previous()) {
    cout << rs -> getString("CityName") << endl;
}

 

如果列名/列標簽無效,或出現數據庫訪問錯誤,或在已關閉的ResultSet對象上調用getString(),getString()都會拋出SQLException.

 

(未完待續)


免責聲明!

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



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