1. 程式人生 > >Developing DataBase Applications Using MySQL Connector/C++ 中文文字

Developing DataBase Applications Using MySQL Connector/C++ 中文文字

Developing DataBase Applications Using MySQL Connector/C++ 中文文字

​ by grayondream

翻譯自mysql Connector C++幫助文件[http://download.csdn.net/detail/midle110/4931166]
​ 本教程將利用從MySQL資料庫中連結,插入和檢索資料的簡單示例向您展示構建和安裝MySQL Connector / C ++驅動程式的基本步驟。因為本文的重點是使用C++ Connector連結資料庫,也就是從C++應用程式的連結,因此現假定您的客戶機上已經安裝某種MySQL資料庫或者可以訪問某種MySQL資料庫。

​ 本教程的目標讀者是那些初次接觸MySQL Connection/C++但熟悉C++語言開發和MySQL資料庫的開發人員。

​ 下面列出了本教程中用於編譯,構建和執行示例的工具和技術:

Database (資料庫版本)
C++ Driver (C++驅動器)
MySQL Client Library (MySQL客戶端庫)
Compiler (編譯器)
Make (make工具)
Operating System (作業系統)
CPU / ISA
Hardware (硬體)

基於JDBC 4.0規範的MySQL C ++規範

​ MySQL聯結器/ C ++是由Sun Microsystems開發的最新的MySQL聯結器之一。用於C ++的MySQL聯結器提供面向物件的應用程式程式設計介面(API)和用於將C ++應用程式連線到MySQL伺服器的資料庫驅動程式。

​ 與C ++的現有驅動程式相比,Connector / C ++的開發採用了不同的方法
在C ++世界中實現JDBC API。換句話說,Connector / C ++驅動程式的介面大都是基於的Java程式語言的JDBC API。Java資料庫連線(JDBC)API是Java程式語言與各種資料庫之間連線的行業標準。Connector / C ++實現了大量的JDBC 4.0規範。 熟悉的C ++應用程式開發人員使用JDBC程式設計可能會發現這是有用的,因為可以提高應用程式開發效率。

​ 以下類是由MySQL Connector / C ++實現的:

Driver
Connection
Statement
PreparedStatement
ResultSet

​ 這個Connector / C ++驅動程式可用於連線到MySQL 5.1及更高版本。在MySQL Connector / C ++之前,C ++應用開發人員需要使用非標準和直接使用MySQL C API或MySQL ++ API,之前用到的是MySQL C API的一個C ++包裝器。

如何安裝MySQL Connector/C++

### 安裝檔案安裝

​ 從版本1.0.4開始,Connector / C ++為Solaris,Linux,Windows,FreeBSD,
Mac OS X,HP-UX和AIX這些平臺提供二進位制安裝包。MSI安裝程式和沒有安裝程式的二進位制zip檔案可以在Window系統上安裝,而以GNU TAR(tar.gz)存檔格式打包壓縮的二進位制安裝包可以在其他作業系統平臺上使用。您可以從Connector / C ++下載頁面下載預編譯的二進位制檔案。在Windows和其他平臺上安裝非常簡單,只需要在需要安裝的位置解壓縮歸檔檔案即可安裝Connector/C++驅動程式。Connector/C++的兩個靜態連結庫和一個動態連結庫可以在驅動程式的安裝目錄下的lib目錄中找到。如果你打算使用MySQL Connector/C++的動態連結庫版本,請確保執行時連結器能夠找到對應的MySQL客戶端庫。請參閱你的作業系統的對應文件,瞭解修改和擴充套件庫的搜尋路徑的步驟。如果您無法無法修改庫的搜尋路徑,請複製您的應用程式,MySQL Connector/C++驅動程式和MySQL客戶端庫在同一個目錄。這種方法有效的情況是大多數的平臺採用將工程的源目錄作為預設的庫檔案搜尋目錄,之後檢索其他目錄尋找庫檔案的策略。

原始碼安裝

​ 想從原始碼構建連結驅動器的人,請檢視附錄I:從源安裝MySQL Connector / C ++中的詳細說明

執行時依賴關係

​ 因為Connector/C++驅動程式與MySQL Client Library連結,因此使用Connector/C++的應用程式將需要在即將執行的機器上安裝和Connector/C++驅動器匹配的MySQL客戶端程式設計環境支援。

開發C++應用程式的IDE

​ 如果你正在尋找一款整合開發環境(IDE)來開發C++應用程式,考慮使用免費開源的NetBeans開發平臺。NetBeans C++開發包允許C/C++開發人員使用它們指定的編譯器和工具與NetBeans IDE一起在白堊紀上構建相應的開發環境,比如Solaris,Linux,Windows和Mac OS X這些作業系統。相應的C/C++開發包可以讓編輯器對C/C++語法擁有語感,並且提供專案模板,動態類瀏覽器,Makefile支援和偵錯程式功能等。可以使用新功能模組和外掛模組擴充套件C/C++開發包基礎功能。MySQL Connector/C++:如何使用NetBeans6.5(for Dummies)構建客戶端教程有指導使用NetBeans IDE基於Connector/C++的客戶端應用程式。除了上述教程外,網站NetBeans.org上的安裝和配置C/C++開發環境的教程有助於你的安裝和配置過程,NetBeans C/C++開發入門教程提供了通過NetBeans上的C/C++開發包開發C/C++應用程式的基本步驟。

為例程在test資料庫中建立City表

​ 本教程的程式碼示例嘗試從MySQL test資料庫中的City表中檢索資料。通過使用MySQL客戶端,這裡顯示了表結構和City表中的資料。MySQL伺服器執行的預設埠為3306。

​ (下面是原文中檢視當前資料庫中的樣表City的過程,不同之處是原文在Linux下進行的我這裡實在Windows下進行的,另外文中提到的樣例表City在test資料庫中,但我個人的MySQL5.7中City表在world資料庫中,有MySQL基礎的應該都能看懂。)

這裡寫圖片描述

這裡寫圖片描述

測試MySQL資料庫與Connector/C++的連結狀態

​ 下面的C++原始碼展示了在同一主機上如何使用MySQL Connector C++連線MySQL伺服器。程式碼示例通過有Connector C++提供的JDBC類API連線到MySQL上的test資料庫執行從表City中檢索所有行,從結果集中提取資料並將其顯示到標準輸出上,使用準備的SQL語句將資料插入到City表格中,使用儲存點顯示事件(transaction,可能翻譯有錯)並檢查結果集和資料庫元資料等操作。

​ 示例程式碼僅用作演示。並不建議讀者採用相應的特定的編碼風格。為了保持簡單,示例程式碼假定使用者始終提供格式正確的輸入,因此在以下示例中沒有明確的錯誤檢查程式碼。 酌情決定是否重新使用示例程式碼。

​ 下面是原文中的MySQLConnectorC++Client.cpp 原始碼

//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()

下面列出的是例程中顯示的編譯和執行環境,以及執行結果,因為本人的執行環境和文中不同因此就不進行相關的演示截圖(#代表linux下的bash命令)

# 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: [email protected]
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伺服器的連線

​ 通過檢索sql:Driver物件的例項來建立與MySQL 伺服器之間的連線。sql::Driver物件由sql::Driver::get_driver_instance方法返回,sql::Driver::connect方法返回sql::Connection物件。

注意:

​ 在上面段落中使用了 :: :: 符號來顯示
合格的方法名稱。例如,在sql :: Driver :: get_driver_instance()中,sql是名稱空間,Driver是類名,get_driver_instance()是方法的名稱。在使用Connector / C ++的C ++應用程式中,可以包括“using namespace sql;” 在你的程式碼的頂部,以避免字首所有的聯結器/ C ++宣告與“sql ::”。 對於本教程的其餘部分,為了簡化名稱空間sql將從所有Connector / C ++特定的宣告中省略。Driver :: get_driver_instance和Driver :: connect方法的定義如下所示。 檢查Connector / C ++安裝中的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發放有兩種形式。第一種形式中,connect接受資料庫連線URL以及資料庫使用者名稱和密碼。第二種形式接受包含連結URL,資料庫使用者名稱和密碼作為鍵值對的std::map。你可以為連線指定”tcp://[hostname[:port]][/schemaname]”(error)使用TCP/IP連線到MySQL伺服器。例如,tcp://127.0.0.1:5555/some_schema。主機名和埠號都是可選的,預設值是127.0.01和3306.在執行時,localhost將自動轉換為127.0.0.1。在連線URL中執行模式名稱也是可選的,如果為進行設定,請確保使用Connection::setSchema方法選擇資料庫模式。

​ 如果你要使用UNIX域套接字到本地主機上執行的MySQL伺服器上,請為資料庫連線URL執行”unix://path/to/unix_socket_file”。例如unix:///tmp/mysql/mysql.sock.在Windows上,您可以使用命名管道連線到本地主機上執行的MySQL伺服器,方法是為資料庫連線URL指定字串“pipe:// path / to / the / pipe”。啟用支援
命名管道,您必須使用–enable-named-pipe選項啟動MySQL伺服器。 如果不指定使用伺服器選項的管道名稱 - 套接字=名稱,將自動建立具有預設名稱MySQL的命名管道。 管道的名稱在Microsoft Windows上不區分大小寫。

​ 以下程式碼片段嘗試連線到預設的localhost上執行的MySQL伺服器埠3306,使用資料庫使用者名稱root,密碼admin和模式名稱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標頭檔案中定義。 包含標頭檔案的時候使用此替代程式碼編譯示例。

..
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++語句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模板類時,您不需要使用delete操作符顯式釋放記憶體。 例如,delete con;要使用auto_ptr,您需要包含標頭檔案。 包含該標頭檔案將允許訪問std名稱空間,也即是模板類auto_ptr 駐留的地方。 其中的type是您希望指標指向的資料/物件型別。

​ 採用auto_ptr智慧指標機制或動態記憶體管理的傳統機制的選擇由讀者自行決定。

獲取一個Statement物件

​ 當Connection :: createStatement方法被呼叫時,它將返回可以用來SQL語句傳送到資料庫伺服器的Statement物件。 使用Statement物件通常執行不帶引數的SQL語句。 換句話說,Statement物件用於執行靜態SQL語句並返回它產生的結果。 如果同一SQL語句必須多次執行不同的輸入,則考慮在執行時使用Prepared語句來提高效率。

​ 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語句之前,必須選擇適當的資料庫模式。 要選擇資料庫模式,請使用模式名稱作為引數呼叫Connection物件的setSchema方法。

執行SQL語句

​ 要執行SELECT語句,請使用SQL語句作為引數呼叫Statement :: executeQuery方法。 executeQuery()返回一個ResultSet物件。 TheStatement :: executeUpdate方法可用於執行給定的SQL語句,這些SQL語句可能是INSERT,UPDATE,DELETE或任何不返回任何SQL SQL語句(如SQL DDL語句)。 與executeQuery()不同,executeUpdate方法不返回ResultSet。 相反,它返回受INSERT,UPDATE或DELETE語句影響的行數。

​ 如果您不提前知道SQL語句是否為SELECT或INSERT,UPDATE或DELETE,請使用execute方法。 如果SQL查詢是SELECT,那麼execute()返回true,如果該語句是INSERT,UPDATE或DELETE,則返回false。 如果語句是SELECT查詢,則可以通過在Statement例項上呼叫getResultSetmethod來檢索結果。 如果語句是INSERT,UPDATE或DELETE語句,則可以通過呼叫getUpdateCount()來檢索受影響行的計數。

​ 在某些不尋常的情況下,單個SQL語句可能會返回多個結果集和/或更新計數。 通常你可以忽略這一點,除非你執行的一個儲存過程,你知道可能會返回多個結果,或者你正在動態執行一個未知的SQL語句。 在這種情況下,請使用getResultSet或getUpdateCount方法檢索結果,並使用getMoreResults()來確定是否存在另一個結果集。

​ 某些相關方法的定義如下所示。 有關Connection和Statement介面支援的完整方法列表,請檢查Connector / C ++安裝中的connection.h和statement.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,因此請確保在程式碼中捕獲這些異常。 為了簡單起見,示例中的程式碼片段不被try .. catch塊包圍。 如果您再次檢視完整的示例程式碼,您將看到目標是從測試資料庫中的City表中檢索所有行。 因此,在示例程式碼中使用了executeQuery(),如下所示:

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

​ executeQuery方法返回一個ResultSet物件,該物件包含給定查詢生成的資料。 此方法在資料庫訪問錯誤的情況下丟擲SQLException,或者如果在閉合語句上呼叫此方法,或者給定的SQL語句生成除單個ResultSe物件之外的任何內容。

​ 或者,以前的程式碼片段可以使用Statement :: execute()重寫,如下所示

bool retvalue = stmt -> execute ("SELECT * FROM City");
if (retvalue) {
res = stmt -> getResultSet();
} else {
...
}

​ 如果第一個結果是ResultSet物件,那麼方法execute將返回true,如果是更新計數或沒有結果,則返回false。 如果execute()返回true,則使用getResultSet方法檢索結果集。 如果結果是更新計數,或者沒有更多結果,則getResultSet()返回NULL。 每個結果只能呼叫getResultSet方法一次。
​ 在執行資料庫訪問錯誤的情況下,或者在已關閉的語句中呼叫此方法時,方法execute和getResultSet都丟擲SQLException。
​ 如果要將新記錄插入資料庫,可以在executeUpdate方法的幫助下執行,如下例所示:

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

​ 方法executeUpdate執行給定的SQL資料操作語言(DML)語句(如INSERT,UPDATE或DELETE)或不返回任何內容的SQL語句(如DDL語句)。 executeUpdate()返回INSERT,UPDATE或DELETE語句的行數,0表示沒有返回的SQL語句。
​ executeUpdate()在資料庫訪問錯誤的情況下丟擲SQLException,或者在關閉的Statement或者給定的SQL語句生成一個ResultSet物件時呼叫它。
​ 使用execute和getUpdateCount方法重寫上述程式碼片段的另一種方法:

int updateCount = 0;
bool retstatus = stmt -> execute ("INSERT INTO City (CityName) VALUES
('Napier, New Zealand')");
if (!retstatus) {
updateCount = stmt -> getUpdateCount();
} else {
...
}

​ 如果第一個結果是更新計數,或者沒有結果,那麼方法execute將返回false。 如果第一個結果是更新計數,則使用getUpdateCount方法檢索該值。 getUpdateCount()返回 - 如果當前結果是ResultSet物件,或者沒有更多結果,則返回-1。 每個結果只應呼叫一次getUpdateCount()。
方法,執行和getUpdateCount都丟擲SQLException,以防資料庫訪問錯誤或者如果在已關閉的語句中呼叫。

從結果集中檢索資料

​ 前面的段落解釋說,執行SQL查詢的方法executeQuery和execute返回ResultSet的一個例項。 您可以使用ResultSet物件來訪問通過針對資料庫執行查詢而返回的資料。 每個ResultSet物件維護一個遊標,指向其當前的資料行。 結果集遊標中的行按順序檢索。 在一行中,可以以任何順序訪問列值。 您可以通過其位置(偏移量)或其名稱/標籤來引用列,儘管後者在某種程度上是容錯的,特別是當表模式發生更改時。 引用列標籤或名稱使程式碼清晰。 另一方面,使用列索引或位置參考可以提高效能。

​ 列標籤是使用SQL“AS”子句指定的列的標籤。 如果未指定SQL“AS”子句,則該標籤是列的名稱。 例如,CN是SQL語句中的列標籤:SELECT CityName AS CN FROM City。 儲存在ResultSet中的資料可以通過使用各種getXX方法( getString()或getInt())來檢索,這取決於正在檢索的資料型別。 使用ResultSet物件的下一個先前的方法將游標移動到結果集中的下一行和上一行。

​ 即使生成它的Statement物件被關閉,重新執行或用於從多個結果序列中檢索下一個結果,ResultSet物件也保持開啟狀態。 一旦結果集已被拉出宣告,ResultSet物件將保持有效,直到它被明確或隱式關閉,而不管生成它的Statement物件的狀態如何。
​ 在這種寫法中,MySQL Connector / C ++返回了Statement物件的緩衝結果。 緩衝結果集快取在客戶端上。 無論結果集多大,驅動程式將始終獲取所有資料。 聯結器的未來版本預計將為Statement物件返回緩衝和無緩衝的結果。
​ 某些相關方法的簽名如下所示。 有關ResultSet介面支援的完整方法列表,請檢視Connector / C ++安裝中的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;

​ 在示例C ++程式碼中,查詢“SELECT * FROM City”返回一個ResultSet,只有一列CityName的資料型別為String,在MySQL中稱為VARCHAR。
以下程式碼片段迴圈遍歷ResultSet物件res,通過引用精確的列名稱從每一行檢索CityName,並將其顯示在標準輸出中:

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

​ 因為也可以通過其位置引用列,以下程式碼片段產生類似的結果:

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

​ getString()的整數引數是指在查詢中指定的列列中的列的位置,以第一個欄位為1。
​ getString()的兩個版本都返回列值。 如果列值為SQL NULL,則返回的值為空字串。 您可以使用列偏移或列名稱/標籤作為引數的ResultSet :: isNull方法來檢查要獲取的列值是否為SQL NULL。 要確定最後一列讀取的值是否為SQL NULL,請呼叫沒有引數的ResultSet :: wasNull。
​ 以下示例顯示如何以相反的順序遍歷游標來獲取資料。

/* 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;
}

​ 如果列標籤/名稱無效或列索引無效,資料庫訪問錯誤或者如果此方法在關閉的結果集上被呼叫,getString方法將丟擲SQLException。

使用準備語句

​ MySQL 4.1引入了準備語句來完成重複執行查詢的任務,儘管每個迭代中都有不同的引數。 準備語句可以通過將SQL邏輯與所提供的資料分開來增強安全性。 邏輯和資料的這種分離可以幫助防止稱為SQL注入攻擊的非常常見的漏洞型別。 但是請注意,雖然準備好的語句可以提高安全性,但應用程式開發人員仍然有責任防範安全攻擊,並在提交到資料庫進行處理之前對輸入進行清理。

​ 已準備好的語句經過優化,可用於處理可從預編譯中受益的引數化SQL語句。 與Statement物件不同,它在建立時提供給PreparedStatement物件。 在大多數情況下,SQL語句立即傳送到資料庫伺服器,查詢解析器執行語法檢查,句法優化,最後解析(編譯)SQL以備以後由MySQL伺服器使用。 因此,PreparedStatement物件包含已預編譯的SQL語句。 這意味著當執行準備好的語句時,資料庫伺服器可以執行PreparedStatement SQL語句,而不必首先編譯它。 減少查詢解析可能會導致MySQL伺服器的效能提升顯著。

​ MySQL客戶端/伺服器協議支援將資料庫結果傳送給客戶端的兩種方法:以文字形式和二進位制形式傳送。 在通過網路傳送文字之前,文字協議始終將資料轉換為字串,伺服器將字串解碼為適當的資料型別。 與文字協議不同,二進位制協議避免了儘可能將資料轉換為字串。 二進位制協議僅用於準備好的語句。 基於通過網路傳送的資料,準備語句使用的二進位制協議可以通過消除字串在客戶端和伺服器上的正確資料型別的編碼和解碼來減少CPU和網路開銷。

​ Connector / C ++基於MySQL C API和C庫libmysql。 因此,它繼承了MySQL Server和MySQL C API的所有限制。 以下語句可用作Connector / C ++的準備語句:CALL,CREATE TABLE,DELETE,DO,INSERT,REPLACE,SELECT,SET,UPDATE以及大多數SHOWSTATE。 USE不支援準備的語句協議,Connector / C ++不包括USE語句的準備語句模擬。 檢查MySQL C API Prepared語句文件,瞭解可以準備的語句的完整列表。
​ 雖然PreparedStatement物件可以與無引數的SQL語句一起使用,但您可能經常使用引數化的SQL語句。

建立一個PreparedStatement物件

​ 就像Statement物件一樣,您可以使用Connection例項建立PreparedStatement物件。 根據活動資料庫呼叫prepare Statement方法Connection物件建立一個PreparedStatement物件,用於將引數化的SQL語句傳送到資料庫。
方法定義:

/* connection.h */
PreparedStatement * Connection::prepareStatement(const std::string& sql) throws
SQLException;

​ preparedStatement()返回一個包含預編譯SQL語句的新的預設PreparedStatement物件。 這種方法在資料庫訪問錯誤的情況下丟擲SQLException或者在關閉的連線上呼叫此方法時丟擲SQLException。
​ 示例C ++程式碼中的以下程式碼片段建立了PreparedStatement物件。

Connection *con;
PreparedStatement *prep_stmt;
..
prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");

PreparedStatement引數的供應值

​ 在引數化SQL語句的情況下,您需要提供要用於替代問號佔位符的值,然後才能執行SQL語句。 您可以通過呼叫PreparedStatement類中定義的setXX方法來執行此操作。 用於設定IN引數值的setter方法(setXX())必須指定與定義的SQL型別的輸入引數相容的型別。 例如,如果IN引數具有SQL型別INTEGER,則應使用setInt()。

​ 示例C ++程式碼中的以下程式碼片段將問號佔位符設定為值為“London,UK”的C ++ std :: string。

PreparedStatement *prep_stmt;
..
prep_stmt -> setString (1, "London, UK");

在PreparedStatement物件中執行SQL語句

​ 類似於使用Statement物件執行SQL語句,您可以使用executeQuery方法執行SELECT語句executeUpdate方法來執行可能是INSERT,UPDATE,DELETE或SQL語句的SQL語句,這些語句不返回任何SQL DDL語句 ,並執行方法來執行任何型別的SQL語句。 檢查執行SQL語句段落的更多細節。
某些相關方法的定義如下所示。 有關PreparedStatement介面支援的完整方法列表,請檢視Connector / C ++安裝中的prepared_statement.h標頭檔案。

/* prepared_statement.h */