南阳微网站制作,网站菜单代码,域名收录提交入口,辽源网站建设设计目录MySQL C Driver的实现基于JDBC4.0规范安装MySQL Connector/C运行时依赖C IDE为示例程序创建数据库与数据表使用Connector/C测试数据库连接使用prepared Statements使用事务访问Result Set Metadata访问Database Metadata通过PreparedStatment对象访问参数元数据捕获异常调试…目录MySQL C Driver的实现基于JDBC4.0规范安装MySQL Connector/C运行时依赖C IDE为示例程序创建数据库与数据表使用Connector/C测试数据库连接使用prepared Statements使用事务访问Result Set Metadata访问Database Metadata通过PreparedStatment对象访问参数元数据捕获异常调试/跟踪 MySQL Connector/C更多信息MySQL C Driver的实现基于JDBC4.0规范MySQL Connector/C是由Sun Microsystems开发的MySQL连接器。它提供了基于OO的编程接口与数据库驱动来操作MySQL服务器。与许多其他现存的C接口实现不同Connector/C遵循了JDBC规范。也就是说Connector/C Driver的API主要是基于Java语言的JDBC接口。JDBC是java语言与各种数据库连接的标准工业接口。Connector/C实现了大部分JDBC4.0规范。如果C程序的开发者很熟悉JDBC编程将很快的入门。MySQL Connector/C实现了下面这些类DriverConnectionStatementPreparedStatementResultSetSavepointDatabaseMetaDataResultSetMetaDataParameterMetaDataConnector/C可用于连接MySQL5.1及其以后版本。在MySQL Connector/C发布之前C程序员可以使用MySQL C API或者MySQL访问MySQL。前者是非标准、过程化的C API后者是对MySQL C API的C封装。安装MySQL Connector/C此处略。(译者注用户可以到MySQL的官网[http://dev.mysql.com/downloads/connector/cpp/1.0.html]去下载MySQL Connector/C的安装程序或者只下载dll或者下载源代码自己编译。笔者在Window平台上使用MySQL下载了mysql-connector-c-noinstall-1.0.5-win32这个版本用于调试。)运行时依赖MySQL Connector/C Driver依赖MySQL的客户端库在MySQL安装目录下的lib\opt\libmysql.dll。如果是通过安装程序来安装MySQL Connector/Clibmysql会一并安装如果从官网只下载了dll或源码在使用时程序必须链接到libmysql.dll。C IDE此处略。(译者注原文作者使用NetBean作为C的IED。笔者使用VS2008)为示例程序创建数据库与数据表(译者注此节略掉许多不太重要的内容。)在MySQL中创建test数据库使用下面语句创建数据表CityCreate Table: CREATE TABLE City ( CityName varchar(30) DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETascii然后向City表中添加一些数据。最后表的内容为mysqlSELECT * FROM City;--------------------| CityName |--------------------| Hyderabad, India || San Francisco, USA|| Sydney, Australia |--------------------3 rows in set (0.17 sec)使用Connector/C测试数据库连接下面的代码演示如何使用Connector/C连接到MySQL服务器连接到test数据库执行一个查询获取City表中的数据显示在控制台上使用Prepared Statements向City表插入数据使用savepoints演示事务获取结果集和数据库的元信息例子代码仅仅用于演示不建议读者在实际开发中使用这种样式的代码。(译者注例子代码很长如果看不太明白没关系等阅读完全文之后再回过头来看)#include #include #include #include #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 200using 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 wont 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;} // whilecout 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 wont 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 (i1);cout.width(20);cout res_meta - getColumnTypeName (i1);cout.width(20);cout res_meta - getColumnDisplaySize (i1) 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} // whilecout 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来说明。)下面是get_mysql_driver_instance与connect这两个方法的签名/* 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:string sql::connectpropertyval options);Driver类重载了connect方法一个接收数据库地址的url、用户名和密码的字符串后一个接收一个mapmap中以key/value的形式包含数据库地址、用户名与密码。使用TCP/IP连接到MySql服务器的url字符串的格式如下tcp://[hostname[:port]][/schemaname]。例如tcp://127.0.0.1:5555/some_scehma。hostname和端口号是可选的如果省略默认是127.0.0.1与3306。如果hostname为localhost会被自动转换为127.0.0.1。schemaname也是可选的如果连接字符串中没有设置schema需要在程序中通过Connection::setSchema方法手动设置。在unix系统上可以通过UNIX domain socket连接运行在本地的MySQL服务连接字符串格式为unix://path/to/unix_socket_file例如unix:///tmp/mysql.sock.在windows系统上可以以命名管道的方式连接到运行在本地的MySQL数据库连接字符串格式为pipe://path/to/the/pipe。MySQL服务必须启动允许命名管道连接可以在启动MySQL服务器的时候通过--enable-named-pipe命令行选项来启动该功能。如果没有通过--socketname选项设置命名管道的名称系统默认使用MySQL。在windows上管道的名称是区别大小写的。下面的代码片断尝试连接到本地的MySQL服务器通过3306端口用户名为root密码是000000schema为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;}也可以通过connection的第二个重载方法连接MySQL。ConnectPropertyVal是union类型在connection.h中定义。sql::mysql::MySQL_Driver *driver 0;sql::Connection *conn 0;std::map: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会把两个连在一起的字符串合并成一个字符串)如mp.str.val unix:// /tmp/mysql.sock当建立与服务器之间的连接后通过Connection::setSessionVariable方法可以设置像sql_mode这样的选项。C细节注意点像Connection这样的对象必须在用完之后显式的delete例如sql::Connection *conn driver - connect(tcp://127.0.0.1:3306, root, 000000);// do somethingdelete conn使用使用auto_ptr来维护连接对象的清理 如use namespace std;use namespace sql;auto_ptr Connection con ( driver - connect(tcp://127.0.0.1:3306, root, 000000) );获取Statement对象Statement对象用于向MySQL服务器发送SQL语句。该对象可以通过调用Connection::createStatement方法获得。Statement向MySQL发送一个静态的SQL语句然后从MySQL获取操作的结果我们无法向它提供sql参数。如果要向它传递参数可以使用PreparedStatemenet类。如果相同的SQL语句(只SQL参数不同)要被执行多次建议使用PreparedStatement类。Connection::createStatement的签名如下(关于Connection类所提供的方法列表可以查看connection.h头文件)/* connection.h */Statement* Connection::createStatement();下面的的代码段通过调用Connection对象的createStatemenet来获取一个Statement对象Connection *conn; // Connection对象的引用Statement *stat;Statement stat conn - createStatement();执行SQL语句在执行SQL语句之前应该通过Connection对象的setSchema方法设置相应的Schema(如果没有在数据库地址URL中指定schema)。Statement::executeQuery用于执行一个Select语句它返回ResultSet对象。Statement::executeUpdate方法主要用于执行INSERT, UPDATE, DELETE语句(executeUpdate可以执行所有的SQL语句如DDL语句像创建数据表。)该方法返回受影响记录的条数。如果你不清楚要执行的是像select这样的查询语句还是像update/insert/delete这样的操作语句可以使用execute方法。对于查询语句execute()返回True然后通过getResultSet方法获取查询的结果对于操作语句它返回False通过getUpdateCount方法获取受影响记录的数量。在一些特殊的情况下单条SQL语句(如执行存储过程)可能会返回多个结果集 和/或 受影响的记录数量。如果你不想忽略这些结果通过getResultSet或getUpdateCount方法第一个结果后再通过getMoreResults()来获取其他的结果集。下面是这些方法的签名可以在statement.h头文件中查阅Statement的完整方法列表。/* 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表的所有记录是如此的简单Statement *stmt;ResultSet *res;res stmt - executeQuery (SELECT * FROM City);executeQuery方法返回ResultSet对象它包含了查询的结果。在以下情况下executeQuery会抛出SQLException异常数据库在执行查询时出错在一个关闭的Statement对象上调用executeQuery给出的SQL语句返回的不是一个简单的结果集上面的代码可以用Statement::execute()重写bool retvalue stmt - execute (SELECT * FROM City);if (retvalue){res stmt - getResultSet();}else{...}execute()返回True表示操作的结果是一个ResultSet对象否则结果是受影响记录的数量或没有结果。当返回True时通过getResultSet方法获取结果集在返回False的情况下调用getResultSet方法将返回NULL。当数据库在执行时发生错误或者在一个已关闭的Statement对象上执行execute与getResultSet方法都会抛出SQLException异常。如果要往数据库里添加一条新的记录可以像下面的例子一样简单的调用executeUpdate方法int updateCount stmt - executeUpdate (INSERT INTO City (CityName) VALUES (Napier, New Zealand));如果executeUpdate执行的是像INSERT, UPDATE或DELETE这样的数据操作语句(DML)它返回受影响的记录的数量如果执行的是数据定义语句(DDL)它返回0。在数据库操作失败或者在一个已经关闭的Statement上调用该方法或者给出的SQL语句是一个查询语句(会返回结果集)该方法会抛出SQLException异常。下面的代码使用execute和getUpdateCount方法来生写上面的例子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中排列的记录是有序的(译者注只能按顺序一条一条获取不能跳跃式获取)。(但)在同一行中列值的访问却是随意的可以通过列的位置或者名称。通过列的名称访问列值让代码更清晰而通过位置访问列值则更高效。列的名称通过SQL语句的AS子名设定如果SQL语句中没有使用AS子名列的名称默认为数据表中对应的列名。例如对于SELECT CityName AS CN FROM CityCN就是结果集中列的名称。在ResultSet中的数据可以通过getXX系列方法来获取例如getString(), getInt()XX取决于数据的类型。next()与previous()使游标移到结果集中的下一条或上一条记录。Statement执行SQL语句返回ResultSet对象后ResultSet就变成一个独立的对象与原先的Statement再也没有联系即使Statement对象关闭重新执行其他sql语句或者获取多个结果集中的下一个。ResultSet将一直有效除非显式或隐式地将其关闭。在撰写本文时对于Statement对象MySQL Connector/C总是返回缓存结果这些结果在客户端缓存。不管结果集数据量大小MySQLConnector/C Driver总是获取所有的数据。希望以后的版本中Statement对象能够返回缓存和非缓存的结果集。下面是数据获取方法的签名可以在resultset.h头文件中查看所有ResultSet类支持的方法。/* 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;}如果数据库中该字段的值为NULLgetString将返回一个空的字符串。Result::isNull用于判断指定列在数据库中的值是否为NULL。Result::wasNULL()用于判断最近读取的列的值是否为空。下面的例子演示了通过cursor(游标)倒序读取结果集中的数据/* 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上执行调用该方法。未完待续