原文地址: http://dev.mysql.com/tech-resources/articles/mysql-connector-cpp.html#trx
- Database MySQL Server 5.1.24-rc
- C++ Driver MySQL Connector/C++ 1.0.5
- MySQL Client Library MySQL Connector/C 6.0
- Compiler Sun Studio 12 C++ compiler
- Make CMake 2.6.3
- Operating System OpenSolaris 2008.11 32-bit
- CPU / ISA Intel Centrino / x86
- Hardware Toshiba Tecra M2 Laptop
目录
MySQL C++ Driver的实现基于JDBC4.0规范
- Driver
- Connection
- Statement
- PreparedStatement
- ResultSet
- Savepoint
- DatabaseMetaData
- ResultSetMetaData
- ParameterMetaData
安装MySQL Connector/C++
运行时依赖
C++ IDE
为示例程序创建数据库与数据表
Create Table: CREATE TABLE `City` ( `CityName` varchar(30) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=ascii
使用Connector/C++测试数据库连接
下面的代码演示如何使用Connector/C++连接到MySQL服务器:
- 连接到test数据库;
- 执行一个查询获取City表中的数据,显示在控制台上;
- 使用Prepared Statements向City表插入数据;
- 使用savepoints演示事务;
- 获取结果集和数据库的元信息;
#include <iostream>
#include <map>
#include <string>
#include <memory>
#include "mysql_driver.h"
#include "mysql_connection.h"
#include "cppconn/driver.h"
#include "cppconn/statement.h"
#include "cppconn/prepared_statement.h"
#include "cppconn/metadata.h"
#include "cppconn/exception.h"
#define DBHOST "tcp://127.0.0.1:3306"
#define USER "root"
#define PASSWORD "000000"
#define DATABASE "test"
#define NUMOFFSET 100
#define COLNAME 200
using namespace std;
using namespace sql;
#pragma comment(lib, "mysqlcppconn.lib")
void Demo();
int main(int argc, char *argv[])
{
Demo();
return 0;
}
/* 获取数据库信息 */
static void GetDBMetaData(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;
}
/* 获取结果集信息 */
static void GetResultDataMetaBata(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;
}
/* 打印结果集中的数据 */
static void RetrieveDataAndPrint(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;
}
void Demo()
{
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(DBHOST);
const string user(USER);
const string password(PASSWORD);
const string database(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 */
GetDBMetaData(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 */
RetrieveDataAndPrint (res, NUMOFFSET, 1, string("CityName"));
/* retrieve and display the result set metadata */
GetResultDataMetaBata (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 */
RetrieveDataAndPrint(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: " << 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;
} catch (std::runtime_error &e) {
cout << "ERROR: " << e.what() << endl;
return;
}
return;
}
建立数据库连接
sql::Connection代表到数据库的连接,可以通过sql::Driver来创建。sql::mysql::get_mysql_driver_instance()方法用于获取sql::Driver,通过调用sql::Driver::connect方法来创建sql::Connection对象。(译者注:笔者使用的Connector/C++版本与作者使用的版本不一样,接口方面也有点细微的差别。这里根据笔者使用的最新版本mysql-connector-c++-noinstall-1.0.5-win32来说明。) /* mysql_driver.h */
MySQL_Driver *sql::mysql::get_mysql_driver_instance()
/* mysql_driver.h */
sql::Connection * connect(const std::string& hostName, const std::string& userName, const std::string& password);
sql::Connection * connect(std::map<std::string, sql::ConnectPropertyVal> & options);
下面的代码片断尝试连接到本地的MySQL服务器,通过3306端口,用户名为root,密码是000000,schema为test.
sql::mysql::MySQL_Driver *driver = 0;
sql::Connection *conn = 0;
try
{
driver = sql::mysql::get_mysql_driver_instance();
conn = driver->connect("tcp://localhost:3306/test", "root", "000000");
cout << "连接成功" << endl;
}
catch (...)
{
cout << "连接失败" << endl;
}
if (conn != 0)
{
delete conn;
}
sql::mysql::MySQL_Driver *driver = 0;
sql::Connection *conn = 0;
std::map<std::string, ConnectPropertyVal> connProperties;
ConnectPropertyVal tmp;
tmp.str.val = "tcp://127.0.0.1:3306/test";
connProperties[std::string("hostName")] = tmp;
tmp.str.val = "root";
connProperties[std::string("userName")] = tmp;
tmp.str.val = "000000";
connProperties[std::string("password")] = tmp;
try
{
driver = sql::mysql::get_mysql_driver_instance();
conn = driver -> connect(connProperties);
cout << "连接成功" << endl;
}
catch(...)
{
cout << "连接失败" << endl;
}
if (conn != 0)
{
delete conn;
}
C++细节注意点
像Connection这样的对象,必须在用完之后,显式的delete,例如: sql::Connection *conn = driver -> connect("tcp://127.0.0.1:3306", "root", "000000");
// do something
delete conn
use namespace std;
use namespace sql;
auto_ptr < Connection > con ( driver -> connect("tcp://127.0.0.1:3306", "root", "000000") );
获取Statement对象
Connection::createStatement的签名如下(关于Connection类所提供的方法列表,可以查看connection.h头文件):
/* connection.h */
Statement* Connection::createStatement();
Connection *conn; // Connection对象的引用
Statement *stat;
Statement stat = conn -> createStatement();
执行SQL语句
/* 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();
Statement *stmt;
ResultSet *res;
res = stmt -> executeQuery ("SELECT * FROM City");
bool retvalue = stmt -> execute ("SELECT * FROM City");
if (retvalue)
{
res = stmt -> getResultSet();
}
else
{
...
} execute()返回True表示操作的结果是一个ResultSet对象,否则结果是受影响记录的数量或没有结果。当返回True时,通过getResultSet方法获取结果集,在返回False的情况下调用getResultSet方法,将返回NULL。
int updateCount = stmt -> executeUpdate ("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");
int updateCount = 0;
bool retstatus = stat->execute("INSERT INTO City (CityName) VALUES ('Napier, New Zealand')");
if (!retstatus)
{
updateCount = stat->getUpdateCount();
}
else
{
...
}
从ResultData中获取数据
上面的段落介绍了执行SQL查询的方法:executeQuery和execute,用于获取ResultSet对象。我们可以通过ResultSet访问查询的结果。每一个ResultSet都包含一个游标(cursor),它指向数据集中的当前记录行。ResultSet中排列的记录是有序的(译者注:只能按顺序一条一条获取,不能跳跃式获取)。(但)在同一行中,列值的访问却是随意的:可以通过列的位置或者名称。通过列的名称访问列值让代码更清晰,而通过位置访问列值则更高效。
在ResultSet中的数据,可以通过getXX系列方法来获取,例如:getString(), getInt(),"XX"取决于数据的类型。next()与previous()使游标移到结果集中的下一条或上一条记录。
在撰写本文时,对于Statement对象,MySQL Connector/C++总是返回缓存结果,这些结果在客户端缓存。不管结果集数据量大小,MySQLConnector/C++ Driver总是获取所有的数据。希望以后的版本中,Statement对象能够返回缓存和非缓存的结果集。
/* 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"返回的ResultSet中只包含一列:CityName,数据类型为String,对应MySQL中的VARCHAR类型。这个例子通过next方法循环从结果集中获取CityName值,并显示在控制台上:
while (res -> next())
{
cout << rs -> getString("CityName") << endl;
}
也可以通过位置来获取列值(位置从1开始而非从0开始),下面的代码产生相同的结果:
while (res -> next())
{
cout << rs -> getString(1) << endl;
}
/* 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异常:指定列名或位置不存在;数据库在执行操作时失败;在一个关闭的cursor上执行调用该方法。
未完待续!
本文档详细介绍如何使用MySQL Connector/C++库操作MySQL数据库,包括连接数据库、执行SQL语句、处理结果集等内容。
2915





