码迷,mamicode.com
首页 > 移动开发 > 详细

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

时间:2017-04-22 09:52:16      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:方法   java数据库   插入数据   文档   超出   驱动程序   visual   大小写   计算   

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 (硬件)

目录

  1. 基于JDBC 4.0规范的MySQL C ++规范
  2. 如何安装MySQL Connector/C++
  3. 运行时依赖关系
  4. 开发C++应用程序的IDE
  5. 为例程在test数据库中创建City表
  6. 测试MySQL数据库与Connector/C++的链接状态
  7. 使用准备的语句
  8. 使用事务
  9. 访问结果集元数据
  10. 访问数据库元数据
  11. 从PreparedStatement对象访问参数元数据
  12. 捕获异常
  13. 使用MySQL Connector/C++调试跟踪
  14. 其他信息
  15. 附录I:从源安装MySQL Connector / C ++

基于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: 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服务器的连接

? 通过检索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 */
ResultSet* PreparedStatement::executeQuery();
int PreparedStatement::executeUpdate();
bool PreparedStatement::execute();
ResultSet* PreparedStatement::getResultSet();
uint64_t PreparedStatement::getUpdateCount();

? 所有上述方法抛出SQLException,因此请确保在代码中捕获这些异常。
? 即使生成它的PreparedStatement对象被重新执行,或者用于从多个结果序列中检索下一个结果,ResultSet对象也保持打开状态。 一旦结果集被拉出PreparedStatement,ResultSet对象将保持有效,直到它被明确或隐式地关闭,或者生成它的PreparedStatement对象被关闭,以先到者为准。
? 示例C ++代码中的以下代码片段在PreparedStatement对象中执行SQL语句。

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

? 一般来说,参数值仍然有效用于重复使用语句。 设置参数值将自动清除其以前的值。 但是,在某些情况下,同时释放当前参数值所使用的所有资源是有用的。 这可以通过调用方法来完成,

PreparedStatement::clearParameters

使用事物

? 数据库事务是作为一个单元一起执行的一个或多个语句的集合,因此所有语句都被执行,或者没有一个被执行。 MySQL通过诸如SET autocommit,START TRANSACTION,COMMIT和ROLLBACK之类的语句来支持给定客户端会话内的本地事务。

禁用自动提交模式

? 默认情况下,所有新的数据库连接都处于自动提交模式。 在自动提交模式下,所有SQL语句将作为单独的事务执行并提交。 当语句完成或下一次执行发生时(以先到者为准)发生提交。 在返回ResultSet对象的语句的情况下,当检索到ResultSet对象的最后一行或ResultSet对象已关闭时,该语句将完成。

? 允许将多个语句分组到事务中的一种方法是禁用自动提交模式。 换句话说,要使用事务,Connection对象不能处于自动提交模式。 Connection类提供了setAutoCommit方法来启用或禁用自动提交。 setAutoCommit()的参数为0将禁用自动提交,值为1启用自动提交。

Connection *con;
..
/* disable the autocommit */
con -> setAutoCommit(0);

? 建议仅在想要进入事务模式时禁用自动提交。 这样,您可以避免为多个语句持有数据库锁,从而增加与其他用户冲突的可能性。

提交或回滚事务

? 一旦自动提交被禁用,对交易安全表(如InnoDB和NDBCLUSTER的更改)的更改不会立即被永久保留。 您必须显式调用方法commit以使更改在数据库中永久性或方法回滚以撤消更改。 之前在commit()调用之前执行的所有SQL语句都包含在当前事务中并提交到一起或作为一个单元进行回滚。
? con是活动连接的以下代码片段说明了一个事务。

Connection *con;
PreparedStatement *prep_stmt;
..
con -> setAutoCommit(0);
prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");
prep_stmt -> setString (1, "London, UK");
prep_stmt -> executeUpdate();
con -> rollback();
prep_stmt -> setString (1, "Paris, France");
prep_stmt -> executeUpdate();
con -> commit();

? 在此示例中,连接con的自动提交模式被禁用,这意味着只有在针对此活动连接对象调用方法提交时,才准备好语句prep_stmt。 在这种情况下,尝试使用准备好的语句将两行插入到数据库中,但是通过调用回滚方法而丢弃数据“London,UK”的第一行,而具有数据“Paris,France” 通过调用commit方法插入City表。
? 另一个示例显示禁用自动提交的备用语法,然后显式提交和/或回滚事务。

Connection *con;
Statement *stmt;
..
stmt = con -> createStatement();
//stmt -> execute ("BEGIN;");
//stmt -> execute ("BEGIN WORK;");
stmt -> execute ("START TRANSACTION;");
stmt -> executeUpdate ("INSERT INTO City (CityName) VALUES (‘London, UK‘)");
stmt -> execute ("ROLLBACK;");
stmt -> executeUpdate ("INSERT INTO City (CityName) VALUES (‘Paris, France‘)");
stmt -> execute ("COMMIT;");

? START TRANSACTION或BEGIN语句开始一个新的事务。 COMMIT通过使更改永久性将当前事务提交到数据库。 ROLLBACK通过取消对数据库的更改来回退当前事务。 使用START TRANSACTION,使用COMMIT或ROLLBACK结束事务之前,自动提交将保持禁用状态。 然后,自动提交模式恢复到之前的状态。
? 作为启动事务的START TRANSACTION的别名支持BEGIN和BEGIN WORK。 START TRANSACTION是标准SQL语法,它是推荐的启动ad-hoc事务的方式。

回滚到事务中的保存点

? 用于C ++的MySQL连接器支持在Savepoint类的帮助下设置保存点,这在事务中提供了更好的控制。 Savepoint类允许您将事务分区为逻辑断点,从而提供对多少事务回滚的控制权。
? 按照这种写法,InnoDB和Falcon存储引擎支持MySQL 6.0中的保存点事务。 要使用事务保存点,连接对象不能处于自动提交模式。 当自动提交被禁用时,应用程序可以在事务中设置保存点,然后回滚保存点之后完成的所有工作。 请注意,启用自动提交会使所有现有保存点无效,并且当尝试将未完成的事务回滚到最后一个保存点时,Connector / C ++驱动程序将抛出InvalidArgumentException异常。

? 保存点被命名或未命名。 您可以通过向Savepoint :: setSavepoint方法提供一个字符串来指定保存点的名称。 如果不指定名称,则会为该保存点分配整数ID。 您可以使用Savepoint :: getSavepointName()检索保存点名称。

? 一些相关方法的声明如下所示。 有关Connection,Statement,PreparedStatement和Savepoint接口支持的完整方法列表,请检查Connector / C ++安装中的connection.h,statement.h和prepared_statement.h头文件。

/* connection.h */
Savepoint* Connection::setSavepoint(const std::string& name);
void Connection::releaseSavepoint(Savepoint * savepoint);
void Connection::rollback(Savepoint * savepoint);

? 以下代码片段将一行插入表City,创建一个保存点SAVEPT1,然后插入第二行。 当事务后来回滚到SAVEPT1时,第二个插入被撤消,但是第一个插入保持不变。 换句话说,当交易被提交时,只有包含“伦敦,英国”的行将被添加到表格城市。

Connection *con;
PreparedStatement *prep_stmt;
Savepoint *savept;
..
prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");
prep_stmt -> setString (1, "London, UK");
prep_stmt -> executeUpdate();
savept = con -> setSavepoint ("SAVEPT1");
prep_stmt -> setString (1, "Paris, France");
prep_stmt -> executeUpdate();
con -> rollback (savept);
con -> releaseSavepoint (savept);
con -> commit();

? Connection :: releaseSavepoint方法将Savepoint对象作为参数,并将其从当前事务中删除。 在事务中创建的任何保存点将自动释放,并在事务提交时或整个事务回滚时变为无效。 将事务回滚到保存点将自动释放和无效在有问题的保存点之后创建的任何其他保存点。 一旦保存点被释放,任何在回滚操作中引用它的尝试都会导致抛出SQLException。

访问结果集元数据

? 当正在处理的SQL语句在运行时未知时,ResultSetMetaData接口可用于确定要用于从结果集中检索数据的方法。 ResultSetMetaData提供有关给定结果集的结构的信息。 由ResultSetMetaData对象提供的数据包括结果集中的列数,这些列的名称或标签和类型以及每列的属性以及指定列的表所属的表,模式和目录的名称 。

? 当在ResultSet对象上调用getMetaData()时,它返回一个描述该ResultSet对象的列的ResultSetMetaData对象。一些相关方法的定义如下所示。 有关ResultSetMetaData接口支持的完整方法列表,请查看Connector / C ++安装中的resultset_metadata.h头文件。

/* resultset.h */
ResultSetMetaData * ResultSet::getMetaData() const;
/* prepared_statement.h */
ResultSetMetaData * PreparedStatement::getMetaData() const;
/* resultset_metadata.h */
std::string ResultSetMetaData::getCatalogName(unsigned int columnIndex);
std::string ResultSetMetaData::getSchemaName(unsigned int columnIndex);
std::string ResultSetMetaData::getTableName(unsigned int columnIndex);
unsigned int ResultSetMetaData::getColumnCount();
unsigned int ResultSetMetaData::getColumnDisplaySize(unsigned int columnIndex);
std::string ResultSetMetaData::getColumnLabel(unsigned int columnIndex);
std::string ResultSetMetaData::getColumnName(unsigned int columnIndex);
int ResultSetMetaData::getColumnType(unsigned int columnIndex);
std::string ResultSetMetaData::getColumnTypeName(unsigned int columnIndex);
int ResultSetMetaData::isNullable(unsigned int columnIndex);
bool ResultSetMetaData::isReadOnly(unsigned int columnIndex);
bool ResultSetMetaData::isWritable(unsigned int columnIndex);

? 以下代码片段演示了如何检索所有列名称或标签,它们的数据类型和大小以及它们所属的表名称和模式名称。

ResultSet *rs;
ResultSetMetaData *res_meta;
res_meta = rs -> getMetaData();
int numcols = res_meta -> getColumnCount();
cout << "\nNumber of columns in the result set = " << numcols << 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;
}
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;
//delete res_meta;
delete rs;

? 从版本1.0.5开始,连接器在超出范围时自动清理ResultSetMetaData对象。 缓解客户端将明确删除ResultSetMetaData对象。 由于元数据对象的隐含破坏,客户端将无法直接删除ResultSetMetaData对象。 任何删除ResultSetMetaData对象的尝试都会导致编译时错误。 类似地,使用auto_ptr模板类实例化ResultSetMetaData类型的对象会导致编译时错误。 例如,上述代码在Connector / C ++ 1.0.5及更高版本上在语句delete res_meta;编译失败( 没有注释)。

准备语句和结果集元数据

? PreparedStatement :: getMetaData()检索一个ResultSetMetaData对象,该对象包含有关ResultSet对象的列的信息,该对象将在执行PreparedStatement对象时返回。 因为PreparedStatement对象是预编译的,所以可以知道它将返回的ResultSet对象,而不必执行它。 因此,可以在PreparedStatement对象上调用getMetaData方法,而不是等待执行它,然后在返回的ResultSet对象上调用ResultSet :: getMetaData方法。
仅在Connector / C ++ 1.0.4及更高版本中才支持PreparedStatement :: getMetaData方法。

访问数据库元数据

? 借助DatabaseMetaData接口,可以检索有关数据库结构的一般信息。 DatabaseMetaData公开了大量关于给定数据库的支持和内容的信息。 例如,通过使用DatabaseMetaData接口,可以确定数据库是否支持事务,外部连接是受支持的,允许的并发连接的最大数量在多大程度上保持打开,并支持ResultSet类型。

? 此接口的用户通常是需要发现如何处理基础数据库管理系统(DBMS)的工具。 对于旨在与多个DBMS一起使用的应用程序尤其如此。 例如,一个工具可能会使用getTypeInfo()来找出在CREATE TABLE语句中可以使用哪些数据类型。 用户可以调用supportsCorrelatedSubqueries()并且supportsBatchUpdates()来查看是否可以使用相关的子查询,并允许批量更新。

? 数据库元数据与特定连接相关联,因此DatabaseMetaData类的对象是通过在活动的Connection对象上调用getMetaData方法创建的。
DatabaseMetaData界面中的一些方法的签名如下所示。 有关DatabaseMetaData接口支持的完整方法列表,请查看Connector / C ++安装中的metadata.h标题

ResultSet *DatabaseMetaData::getIndexInfo(const std::string& catalog, const std::string&
schema, const std::string& table, bool unique, bool approximate);
int DatabaseMetaData::getMaxColumnsInIndex();
int DatabaseMetaData::getMaxColumnsInTable();
int DatabaseMetaData::getMaxConnections();
ResultSet *DatabaseMetaData::getSchemas();
ResultSet *DatabaseMetaData::getTableTypes();
bool DatabaseMetaData::supportsTransactions();
bool DatabaseMetaData::supportsBatchUpdates();
bool DatabaseMetaData::supportsMultipleResultSets();
bool DatabaseMetaData::supportsNamedParameters();
bool DatabaseMetaData::supportsSavepoints();
bool DatabaseMetaData::supportsStatementPooling();
bool DatabaseMetaData::supportsStoredProcedures();
bool DatabaseMetaData::supportsUnion();
/* connection.h */
DatabaseMetaData *Connection::getMetaData();
/* metadata.h */
const std::string& DatabaseMetaData::getDatabaseProductName();
std::string DatabaseMetaData::getDatabaseProductVersion();
const std::string& DatabaseMetaData::getDriverName();
const std::string& DatabaseMetaData::getDriverVersion();
ResultSet *DatabaseMetaData::getTables(const std::string& catalog, const std::string&
schemaPattern, const std::string& tableNamePattern, std::list& types);

? 以下代码片段演示了如何使用上述方法从数据库中检索一些元数据。

Connection *dbcon;
/* the following line results in compilation error with Connector/C++ 1.0.5 and later */
//auto_ptr < DatabaseMetaData > dbcon_meta ( dbcon -> getMetaData() );
DatabaseMetaData *dbcon_meta = dbcon -> getMetaData();
cout << boolalpha;
cout << "Database Product Name: " << dbcon_meta -> getDatabaseProductName() << endl;
cout << "Database Product Version: " << dbcon_meta -> getDatabaseProductVersion() << endl;
cout << "Driver name: " << dbcon_meta -> getDriverName() << endl;
cout << "Driver version: " << dbcon_meta -> getDriverVersion() << endl << endl;
cout << "Supports Transactions?: " << dbcon_meta -> supportsTransactions() << endl;
cout << "Supports Named Parameters?: " << dbcon_meta -> supportsNamedParameters() << endl;
cout << "Maximum Connections: " << dbcon_meta -> getMaxConnections() << endl;
cout << "Maximum Columns per Table: " << dbcon_meta -> getMaxColumnsInTable() << endl;
cout << "\nDatabase schemas: " << endl;
auto_ptr < ResultSet > rs ( dbcon_meta -> getSchemas() );
cout << "\nTotal number of schemas = " << rs -> rowsCount() << endl;
int row = 1;
while (rs -> next()) {
cout << "\t" << row << ". " << rs -> getString("TABLE_SCHEM") << endl;
++row;
} // while

? 从版本1.0.5起,连接器在超出范围时自动清理DatabaseMetaData对象。 这将免除客户端明确删除DatabaseMetaData对象。 由于元数据对象的隐含破坏,客户端将无法直接删除DatabaseMetaData对象。 任何删除DatabaseMetaData对象的尝试都会导致编译时错误。 类似地,使用auto_ptr模板类实例化DatabaseMetaData类型的对象会导致编译时错误。

从PreparedStatement对象访问参数元数据

? Connector / C ++部分支持为给定的PreparedStatement对象返回参数元数据。 按照这种写法,Connector / C ++仅支持ParameterMetaData接口中的一个方法getParameterCount。 Connector / C ++的未来版本可能支持JDBC 4.0规范中ParameterMetaData接口中定义的大多数方法。
? getParameterCount方法返回ParameterMetaData对象将包含信息的PreparedStatement对象中的参数数。 在数据库访问错误的情况下,getParameterCount()抛出SQLException。
? 相关方法的定义如下所示。

/* prepared_statement.h */
ParameterMetaData* PreparedStatement::getParameterMetaData();
/* parameter_metadata.h */
int ParameterMetaData::getParameterCount();

? 在Connector / C ++安装中的parameter_metadata.h头中声明了getParameterCount方法。 如果您的代码对ParameterMetaData :: getParameterCount方法的引用,请确保在C ++应用程序中包含parameter_metadata.h头文件。
? 以下代码片段演示了如何从给定的PreparedStatement对象中检索参数计数。

#include <cppconn/driver.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/parameter_metadata.h>
...
...
Driver *driver;
Connection *con;
PreparedStatement *prep_stmt;
ParameterMetaData *param_meta;
try {
driver = get_driver_instance();
con = driver -> connect("tcp://127.0.0.1:3306", "root", "admin");
prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");
param_meta = prep_stmt -> getParameterMetaData();
cout << "Number of parameters in the prepared statement = "
<< param_meta -> getParameterCount() << endl;
//delete param_meta;
} catch (..) {
...
}

? 从版本1.0.5开始,连接器在超出范围时自动清除ParameterMetaData对象。 这将减轻客户端明确删除ParameterMetaData对象。 由于元数据对象的隐含破坏,客户端将无法直接删除ParameterMetaData对象。 任何删除ParameterMetaData对象的尝试都会导致编译时错误。 类似地,使用auto_ptr模板类实例化一个类型为ParameterMetaData的对象导致编译时错误。 例如,上述代码在Connector / C ++ 1.0.5及更高版本中编译语句delete param_meta;时失败( 没有注释)。

清理:释放系统资源

? 如果不再需要对象,则使用C ++删除操作符来销毁该对象。 如果您正在使用auto_ptr模板类动态管理内存资源,则不需要显式释放内存资源。 有关auto_ptr智能指针的更多详细信息,请查看C ++特定注意事项。 在所有其他情况下,请确保您明确释放分配的内存,即使在运行时错误和异常的情况下。 否则会导致内存泄漏。
? 当应用程序的上下文中不再需要它们时,通过关闭文件,销毁线程等来同样地释放系统资源。
? 以下代码片段分别删除ResultSet,Statement,PreparedStatement和Connection对象res,stmt,prep_stmt和con。

Connection *con;
Statement *stmt;
PreparedStatement *prep_stmt;
ResultSet *res;
ResultSetMetaData *res_meta;
DatabaseMetaData *dbcon_meta;
delete res;
delete stmt;
delete prep_stmt;
//delete res_meta;
//delete dbcon_meta;
con -> close();
delete con;

? 从Connector / C ++ 1.0.5版本开始,客户端不再需要直接破坏元数据对象。 驱动程序负责客户端的元数据对象销毁。
? 当在MySQL中禁用自动提交时,如果关闭数据库连接而不明确调用提交方法,则所有未提交的事务将自动回滚。

捕获异常

? 除了std :: runtime_error之外,MySQL Connector / C ++可以抛出四个不同的异常,可以提供有关数据库错误或其他错误的信息。

  1. SQLException,派生自std :: runtime_error

  2. InvalidArgumentException派生自SQLException

  3. MethodNotImplementedException派生自SQLException

  4. InvalidInstanceException派生自 SQLException.

    ? 所有上述异常在Connector / C ++安装的exception.h头文件中定义。

    ? 因为所有这些异常类都直接或间接地从std :: runtime_error派生,所以当方法调用异常对象时,它们可以返回一个C风格的字符串,描述当前错误的一般原因。
    ? SQLException类有两个其他方法的实现 - 1. getSQLState(),它以字符串的形式返回当前的SQL状态; 和2. getErrorCode(),它返回一个与MySQL错误代码相对应的整数错误代码。 检查MySQL参考手册中有关服务器错误代码和消息。
    以下输出显示了连接器/ C ++在输入不正确的情况下抛出异常的能力。

    ? 下面是运行结果(#代表终端输入)

    
    # ./mysqlconnectorc++client localhost giri admin test
    
    ERROR: SQLException in MySQLConnectorC++Client.cpp (main) on line 255
    ERROR: Access denied for user ‘giri‘@‘localhost‘ (using password: YES) (MySQL error code:
    1045, SQLState: 28000)
    
    # ./mysqlconnectorc++client localhost root admin test2
    
    Database connection‘s autocommit mode = 0
    ERROR: SQLException in MySQLConnectorC++Client.cpp (main) on line 255
    ERROR: Unknown database ‘test2‘ (MySQL error code: 1049, SQLState: 42000)
    
    # ./mysqlconnectorc++client ben13.sfbay root admin test
    
    ERROR: SQLException in MySQLConnectorC++Client.cpp (main) on line 255
    ERROR: Unknown MySQL server host ‘ben13.sfbay‘ (1) (MySQL error code: 2005, SQLState:
    HY000)
    When a non-existing column name is specified:
    
    # ./mysqlconnectorc++client localhost root admin test
    
    ...
    InvalidArgumentException: MySQL_ResultSet::getString: invalid value of ‘columnIndex‘
    ...

使用MySQL Connector/C++调试跟踪

? 在出现随机问题的情况下,连接器驱动程序生成的调试跟踪和协议文件对于问题诊断比传统的工具(如调试器)调试客户端应用程序更有用。 MySQL Connector / C ++能够生成两种类型的调试跟踪。

  1. 由MySQL客户端库的调试版本生成的跟踪
  2. 由连接器/ C ++驱动程序内部生成的跟踪

按照这种方式写时,调试跟踪只能在调试版本的MySQL Client Library的情况下通过API调用激活。 这些跟踪是在每个连接的基础上进行控制的。 另一方面,通过在非Windows平台上设置环境变量MYSQLCPPCONN_TRACE_ENABLED,可以为整个客户端应用程序激活连接器的内部跟踪。 在这两种情况下,您可以使用Connection :: setClientOptions()有选择地激活和停用某些函数调用的调试跟踪。 默认情况下,MySQL Client Library跟踪总是写入标准错误(stderr),而Connector / C ++驱动程序协议消息被写入标准输出(stdout)。 请注意,调试跟踪可能包含来自您的应用程序的SQL语句。 打开跟踪功能时要小心,特别是当SQL语句不应在客户端公开时。

? setClient Options方法的定义如下所示。 此方法在Connector / C ++安装中的connection.h头文件中定义。

/* connection.h */
void Connection::setClientOption(const std::string & optionName, const void * optionValue);

? 用于选项名称的字符串“libmysql_debug”在调试版本的MySQL客户端库的情况下启用调试跟踪,其中“clientTrace”字符串在通过Connector / C ++驱动程序进行内部跟踪的情况下启用调试跟踪。

由MySQL客户端库生成的跟踪

? 连接器驱动程序隐式调用C-API函数mysql_debug来启用此调试跟踪。 只有调试版本的MySQL客户端库能够生成调试跟踪。 因此,为了启用跟踪功能,请将Connector / C ++驱动程序与MySQL Client Library的调试版本相链接。 无需使用DMYSQLCPPCONN_TRACE_ENABLE:BOOL = 1 CMake选项来构建驱动程序,或者在运行时设置环境变量MYSQLCPPCONN_TRACE_ENABLED以生成此跟踪。 跟踪显示内部函数调用和内部对象的地址,如下所示:

<cli_read_query_result
>mysql_real_query
| enter: handle: 0x808a228
| query: Query = ‘SELECT CityName as CITYNAME FROM City‘
| >mysql_send_query
| | enter: rpl_parse: 0 rpl_pivot: 1
| <mysql_send_query
| >cli_advanced_command
| | >net_write_command
| | | enter: length: 37
| | <net_write_command
| | >net_flush
| | | <vio_is_blocking
| | | >net_real_write
| | | | >vio_write
| | | | | enter: sd: 4 buf: 0x808fa38 size: 42
| | | | | exit: 42
...
...
Voluntary context switches 25, Involuntary context switches 5
>TERMINATE
| safe: sf_malloc_count: 4
| safe: Memory that was not free‘ed (16420 bytes):
| safe: Maximum memory usage: 65569 bytes (65k)
<TERMINATE

? 以下代码片段演示了如何使用MySQL Client Library和Connector / C ++的Connection :: setClientOptions()为executeQuery函数调用生成调试跟踪。

Driver *driver;
Connection *con;
Statement *stmt;
ResultSet *rs;
string url, user, password;
try {
driver = get_driver_instance();
con = driver -> connect(url, user, password);
stmt = con -> createStatement();
con -> setClientOption ("libmysql_debug", "d:t:O,/tmp/client.trace");
rs = stmt -> executeQuery ("SELECT * FROM City");
con -> setClientOption ("libmysql_debug", "f");
} catch (SQLException &e) {
..
}

? 调试控制字符串“d:t:O,/ tmp / client.trace”指定要采集什么样的调试跟踪。标志’d’使能DBUG_宏的当前状态输出。在MySQL中,使用’d’选项打印的常用标签是ENTER,EXIT,ERROR,WARNING,INFO和LOOP。有关支持的DBUG宏的完整列表,请查看DBUG C程序调试包文档。
? 标志’t’启用函数调用/退出跟踪行。之后可以列出一个数字最大化跟踪级别,超过此列表,调试或追踪宏不会出现任何输出。例如,t,20
标志’O,/ tmp / client.trace’将调试器输出流重定向到客户端计算机上指定的文件/tmp/client.trace。文件在每次写入之间刷新。需要时,文件关闭并在每次写入之间重新打开。
? 标志’f’后跟一个空列表的命名功能将禁用调试跟踪。有关调试控制标志及其描述的完整列表,请参阅“MySQL参考手册”

由连接器/ C ++驱动程序生成的内部跟踪

? 连接器/ C ++驱动程序的内部调试跟踪是一个调用跟踪。 它显示每个函数调用,有时候函数参数和其他相关信息。 但是,默认情况下,Connector / C ++不支持内部跟踪功能。 当构建Connector / C ++驱动程序时,请确保使用CMake选项DMYSQLCPPCONN_TRACE_ENABLE:BOOL = 1启用跟踪模块。 没有必要将应用程序与MySQL Client Library的调试版本进行链接,以生成Connector / C ++的内部跟踪。 本教程的“安装MySQL连接器/ C ++”部分详细说明。 使用启用跟踪功能编译连接器驱动程序将会在每个驱动程序函数调用中产生两个额外的跟踪函数调用开销,如下所示。

# <MySQL_Connection::init
# >MySQL_Connection::setAutoCommit
# <MySQL_Connection::setAutoCommit
Database connection‘s autocommit mode = # >MySQL_Connection::getAutoCommit
Executing query: SELECT * FROM City ..
# >MySQL_Statement::execute
# | INF: this=80694b8
# | INF: query=START TRANSACTION;
# | >MySQL_Statement::do_query
# | | INF: this=80694b8
# | <MySQL_Statement::do_query
# <MySQL_Statement::execute
# >MySQL_Statement::execute
# | INF: this=80694b8
# | INF: query=INSERT INTO City (CityName) VALUES (‘Napier, New Zealand‘)
# | >MySQL_Statement::do_query
# | | INF: this=80694b8
# | <MySQL_Statement::do_query
# <MySQL_Statement::execute
# >MySQL_Statement::getUpdateCount
# <MySQL_Statement::getUpdateCount
update count = 1
# >MySQL_Statement::execute
# | INF: this=80694b8
# | INF: query=ROLLBACK;
# | >MySQL_Statement::do_query
# | | INF: this=80694b8
...

? 当连接器/ C ++驱动程序使用CMake选项构建时,DMYSQLCPPCONN_TRACE_ENABLE:BOOL = 1,非Windows平台上的用户可以通过在运行之前将环境变量MYSQLCPPCONN_TRACE_ENABLED设置为命令行上的任何值来启用整个应用程序的调试跟踪 来自同一shell的C ++客户端应用程序。

bash# MYSQLCPPCONN_TRACE_ENABLED=1 ./mysqlconnectorc++client
# >MySQL_Connection::init
# | INF: hostName=tcp://:
# | INF: user=root
# | INF: port=0
# | INF: schema=
# | INF: socket=
# | >MySQL_Connection::setAutoCommit
# | etAutoCommit
# | >MySQL_Connection::setTransactionIsolation
# | etTransactionIsolation
# nit
...

? 使用环境变量控制调试跟踪很简单,不需要在应用程序中进行任何代码检测。 它从应用程序生成所有驱动程序特定函数调用的跟踪。 但是,如果您希望每个连接调试跟踪或对生成的跟踪进行细粒度控制,请考虑使用Connection :: setClientOptions方法调试代码。 例如,如果要从应用程序调用驱动程序支持的函数时查看驱动程序中发生的情况,请从应用程序调用setClientOptions()两次,一次在启动驱动程序特定功能之前启用客户端跟踪 有兴趣的,第二个呼叫是在调用该功能后禁用客户端跟踪。

? 以下代码片段演示了如何使用Connection :: setClientOptions()为prepareStatement()函数调用生成连接器的内部调试跟踪,

Driver *driver;
Connection *con;
PreparedStatement *prep_stmt;
string url, user, password;
int on_off = 0;
try {
    driver = get_driver_instance();
        con = driver -> connect(url, user, password);
    /* enable client tracing */
    on_off = 1;
    con -> setClientOption ("clientTrace", &on_off);
    prep_stmt = con -> prepareStatement ("INSERT INTO City (CityName) VALUES (?)");
    /* disable client tracing */
    on_off = 0;
    con -> setClientOption ("clientTrace", &on_off);
} catch (SQLException &e) {
..
}

? 对上述代码中所示的setClientOption方法的调用导致将调试跟踪写入stdout。 在这种情况下,不需要设置环境变量MYSQLCPPCONN_TRACE_ENABLED。 请注意,启用跟踪的版本可能会导致更高的CPU使用率,即使您的应用程序的整体运行时间不会受到显着影响。 如果调试跟踪必须写入跟踪文件,将会有一些额外的I / O开销。

其他信息

? 这里原文是一些教程和博客博客,可以在官网上找到对应的手册等信息。

致谢

? 作者要感谢安德烈·赫里斯托夫,乌尔夫·温德尔,兰斯·安徒生和埃德温·德苏萨 MySQL AB,对本文的广泛反馈

关于作者

? Giri Mandalika是Sun的ISV工程组织的一名软件工程师。 Giri与合作伙伴和ISV合作,使Sun成为部署应用程序的首选供应商。 目前,Giri专注于Sun平台上企业应用程序的标准基准测试,优化和可扩展性

附录I:从源安装MySQL Connector / C ++

? MySQL连接器/ C ++要求安装MySQL 5.1 [或更高版本]客户端编程支持 - MySQL特定的库和头文件。 默认情况下,不同平台上的大多数MySQL服务器安装方法都安装必要的文件。 但是,如果您在Linux上从RPM文件安装MySQL,请确保已安装开发人员RPM。 客户端程序在客户端RPM中,但客户机编程支持在开发人员RPM中。

? 如果您没有安装MySQL服务器,并且如果您只需要MySQL客户端库以及头文件,请考虑在构建机器上安装MySQL Connector / C,以进行所需的客户机编程支持。 MySQL Connector / C是用于客户端 - 服务器通信的C客户端库。 它是MySQL服务器随附的MySQL Client Library的独立替代品。 在撰写本文时,MySQL Connector / C 6.0以源代码形式以及Solaris,Linux,Windows,FreeBSD,Mac OS X,HP-UX和AIX平台的二进制形式提供。 MSI安装程序和没有安装程序的二进制zip文件可用于Windows,其中二进制包可用作其余平台的压缩GNU TAR存档(tar.gz)。 您可以从Connector / C 6.0下载页面下载源代码和预编译的二进制文件。 从源代码构建Connector / C驱动程序非常类似于从源代码构建Connector / C ++驱动程序。

? 如果Connector / C不符合您的要求,请下载MySQL,然后安装。

? 将MySQL客户端库的位置添加到运行时链接器用于查找动态依赖关系的搜索路径。 例如,在Solaris OS上,使用LD_LIBRARY_PATH环境变量设置运行时链接程序搜索路径,如下所示:

Connector/Cexport LD_LIBRARY_PATH=/export/expts/MySQLConnectorC6.0/lib:$LD_LIBRARY_PATH
               #= OR =
MySQL Server
# ls -1 /opt/coolstack/mysql_32bit/lib/mysql/libmysqlclient_r.so.16.0.0
/opt/coolstack/mysql_32bit/lib/mysql/libmysqlclient_r.so.16.0.0
# export LD_LIBRARY_PATH=/opt/coolstack/mysql_32bit/lib/mysql:$LD_LIBRARY_PATH
# ls -1 /export/expts/MySQLConnectorC6.0/lib/libmysqlclient_r.so
/export/expts/MySQLConnectorC6.0/lib/libmysqlclient_r.so
#

Build & Install MySQL Connector/C++

? 以下分步说明适用于所有受支持的UNIX平台。 检查MySQL Forge Wiki和MySQL Connector / C ++:使用Visual Studio(for Dummies)页面构建Windows客户端的Windows构建指导。

  1. 从dev.mysql.com下载最新版本的MySQL Connector / C ++源代码树。

  2. 提取源文件

    
    # gunzip -c mysql-connector-c++-1.0.4-beta.tar.gz | tar -xvf -
    
    
    # cd mysql-connector-c++-1.0.4-beta
    
  3. 如果您不使用GNU C / C ++编译器,则需要通过设置环境变量CC和CXX来告诉Make build系统要使用哪个编译器。
    例如,要在Solaris和Linux平台上使用Sun Studio C / C ++编译器,请按如下所示设置环境变量CC和CXX。

    
    # which cc
    
    /usr/bin/cc
    
    # which CC
    
    /usr/bin/CC
    
    # export CC=cc
    
    
    # export CXX=CC
    
  4. 运行CMake,跨平台构建系统,生成Makefile。 在此步骤中,您可以指定编译器和链接编辑器选项。 此外,您可以在此步骤中指定各种选项,如安装目录,构建类型 - 调试或非调试,mysql_config的位置。 对于所有支持的选项和确切的语法,请检查“CMake文档”。

    
    # cmake -DCMAKE_REQUIRED_FLAGS=-xO4 -DCMAKE_INSTALL_PREFIX=/export/expts/
    
    MySQLConnectorC++

    在非Windows系统上,如果CMake变量MYSQL_CONFIG_EXECUTABLE未设置,CMake会尝试在默认位置找到mysql_config文件。 如果MySQL服务器安装在非默认位置,请设置CMake变量MYSQL_CONFIG_EXECUTABLE

    
    # other options ..]cmake -DMYSQL_CONFIG_EXECUTABLE=/path/to/my/mysql/server/bin/mysql_config [..
    

    在Windows平台上,mysql_config不可用。 因此,CMake尝试从环境变量ENVMYSQLDIRMySQLMYSQLDIRCMakeMySQL ENV {ProgramFiles} / MySQL / * / include和$ ENV {SystemDrive} / MySQL / * / include。
    要构建MySQL Connector / C ++的调试版本,请将CMake变量CMAKE_BUILD_TYPE设置为Debug。

    
    # cmake -DCMAKE_BUILD_TYPE=Debug [.. other options ..]
    

    MySQL Connector / C ++驱动程序能够生成调试跟踪。 默认情况下,连接器的内部跟踪功能不可用。 如果需要,您必须使用CMake选项DMYSQLCPPCONN_TRACE_ENABLE:BOOL = 1在编译时启用跟踪模块。 跟踪功能可用于连接器的调试和非调试版本,因此不需要构建MySQL Connector / C ++驱动程序的调试版本,以便使用连接器的内部跟踪功能。
    要启用跟踪模块,请将类型为BOOL的CMake变量MYSQLCPPCONN_TRACE_ENABLE设置为1。

    
    # cmake -DMYSQLCPPCONN_TRACE_ENABLE:BOOL=1 [.. other options ..]
    

    除了内部调试跟踪之外,Connector / C ++还可以在与MySQL Client Library的调试版本链接时生成调试跟踪。 使用MySQL Connector / C ++检查调试跟踪以了解更多详细信息。
    运行cmake -L检查CMake使用的选项列表。

  5. 运行make来构建Connector / C ++驱动程序

    
    #make
    
  6. 最后运行make安装选项在指定位置安装Connector / C ++驱动程序。

    
    # make install
    
    ..
    -- Installing: /export/expts/MySQLConnectorC++/lib/libmysqlcppconn.so.1
    -- Installing: /export/expts/MySQLConnectorC++/lib/libmysqlcppconn-static.a
    -- Installing: /export/expts/MySQLConnectorC++/include/mysql_connection.h
    -- Installing: /export/expts/MySQLConnectorC++/include/mysql_driver.h
    ..

    如果在Makefile生成期间未设置CMake变量CMAKE_INSTALL_PREFIX(步骤#4),驱动程序与头文件一起将安装在/ usr / local的默认位置。

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

标签:方法   java数据库   插入数据   文档   超出   驱动程序   visual   大小写   计算   

原文地址:http://blog.csdn.net/grayondream/article/details/70244833

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!