65.9K
CodeProject 正在变化。 阅读更多。
Home

访问:使用 MySQL C API 访问 MySQL 数据库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (21投票s)

2009 年 4 月 1 日

CPOL

6分钟阅读

viewsIcon

104959

downloadIcon

7302

一组用于使用 MySQL C API 访问和操作 MySQL 数据库的类

1. 目标读者

本文档是为初学者编写的,适合那些不知道如何使用 MySQL C API 访问 MySQL 数据库并希望获得简要介绍的开发人员。

2. 简介

如今,MySQL 数据库因其高性能、高可靠性、易用性和显著的成本效益而成为世界上最流行的开源数据库。

它在全球提供的 UNIX 主机套餐中非常受欢迎,因此在本文中,我将展示如何访问这个非常强大的关系数据库系统。

我们将把提供的 MySQL C API 封装在一组 C++ 类中,并在下一篇文章中,我将使用这些相同的类来处理 PostgreSQL。

好了,现在我们开始讲 MySQL。

3. 可用的 API 和库

MySQL 数据库提供了丰富的 API 和库来访问和操作数据及数据库,请参阅

需要注意的是,C API 是覆盖和文档最全面的,因为它是由 MySQL 团队开发的,我们将使用它来构建我们的 C++ 类集。

4. 获取 MySQL

最近 MySQL Labs 被 Sun Microsystems 收购,因此获取它的最简单方法以及了解最新消息的方法是访问

MySQL 在许多平台上都可用,在我编写本文档时,版本 5 可用于

  1. Windows
  2. Windows x64
  3. Linux (非 RPM 包)
  4. Linux (非 RPM,Intel C/C++ 编译,glibc-2.3)
  5. Red Hat Enterprise Linux 3 RPM (x86)
  6. Red Hat Enterprise Linux 3 RPM (AMD64 / Intel EM64T)
  7. Red Hat Enterprise Linux 3 RPM (Intel IA64)
  8. Red Hat Enterprise Linux 4 RPM (x86)
  9. Red Hat Enterprise Linux 4 RPM (AMD64 / Intel EM64T)
  10. Red Hat Enterprise Linux 4 RPM (Intel IA64)
  11. Red Hat Enterprise Linux 5 RPM (x86)
  12. Red Hat Enterprise Linux 5 RPM (AMD64 / Intel EM64T)
  13. SuSE Linux Enterprise Server 9 RPM (x86)
  14. SuSE Linux Enterprise Server 9 RPM (AMD64 / Intel EM64T)
  15. SuSE Linux Enterprise Server 9 RPM (Intel IA64)
  16. SuSE Linux Enterprise Server 10 RPM (x86)
  17. SuSE Linux Enterprise Server 10 RPM (AMD64 / Intel EM64T)
  18. SuSE Linux Enterprise Server 10 RPM (Intel IA64)
  19. Ubuntu 6.06 LTS (Dapper Drake)
  20. Linux x86 通用 RPM (静态链接到 glibc 2.2.5)
  21. Linux x86 通用 RPM (动态链接)
  22. Linux AMD64 / Intel EM64T 通用 RPM
  23. Linux Intel IA64 通用 RPM
  24. Solaris (pkgadd 包)
  25. Solaris (TAR 包)
  26. FreeBSD (TAR 包)
  27. Mac OS X (包格式)
  28. Mac OS X (TAR 包)
  29. HP-UX (depot 包)
  30. HP-UX (TAR 包)
  31. IBM AIX
  32. IBM i5/OS (SAVF 包)
  33. IBM i5/OS (TAR 包)
  34. QNX
  35. Novell NetWare
  36. SCO OpenServer 6

要获取更新列表和直接下载,请访问 https://dev.mysqlserver.cn/downloads/mysql/5.0.html#downloads

注意: 在您选择的平台上安装 MySQL 时,务必安装开发组件(C 包含文件/库文件),其中包含所需的链接库和包含文件,以便遵循本教程。

5. C++ 接口

该接口集由以下 4 个类组成:

物理文件

1

Exception.h

DataBaseError

2

DataBase.h

DataBase

3

ResultSet.h

ResultSet

4

MySql.h

MySql

5.1 DataBaseError 类

class DataBaseError : public std::exception 
/// DataBaseError class 
{ 
   public: 
      DataBaseError(const std::string& what) 
      : exception(what.c_str()) 
      { 
      } 
}; // DataBaseError

这个简单的类用于需要抛出接口错误时,它继承自 **‘std::exception’** 并利用其提供的错误处理程序。

5.2 DataBase

template<class T> 
class DataBase 
/// DataBase class 
{ 
   public: 
     DataBase() 
     : _connected(false) 
     { 
     } 

     virtual ~DataBase() 
     { 
        if(_connected) 
           _dataBaseEngine.close(); 
     } 
  
     void connect(const std::string& server, const std::string& user,
         const std::string& password, const std::string& database) 
     { 
        _dataBaseEngine.connect(server, user, password, database); 
        _connected = true; 
     } 
  
 
     DataBase& operator<< (const std::string& sql) 
     { 
        _dataBaseEngine.execute(sql); 
        return *this; 
     } 
  
     DataBase& operator, (ResultSet& rs) 
     { 
        _dataBaseEngine.populate(rs); 
        return *this; 
     } 
 
     private: 

        T _dataBaseEngine; 
        bool _connected; 


}; // DataBase

此类采用策略模式和模板实现,可以在编译时选择算法,然后我们可以这样使用它:

DataBase<MySQL> mySQLdb; 
DataBase<PostgreSQL> postgreSQLdb; 

它重载了运算符:**‘<<’** 和 **‘,’**,第一个用于向数据库发送 SQL 命令,第二个用于当第一个命令是 `SELECT` 时,它会生成一个 `ResultSet` 响应。

5.3 ResultSet

class ResultSet 
{ 
   public: 
   ResultSet() 
      : _current(0) 
   { 
   } 
 
   void addRow(const std::vector<std::string>& row) 
   { 
      _resultSet.push_back(row); 
   } 
 
   bool fetch(size_t field, std::string& fieldValue) 
   { 
      size_t sz = _resultSet.size(); 

      if(sz) 
      { 
          if(sz > _current) 
          { 
              fieldValue = _resultSet[_current++][field]; 
              return true; 
          } 
      }  

     
    _current = 0; 
    return false; 
 
   } 
 
   bool fetch(std::vector<std::string>& rowValue) 
   { 
      size_t sz = _resultSet.size(); 
      if(sz) 
      { 
         if(sz > _current) 
         { 
            rowValue = _resultSet[_current++]; 
            return true; 
         } 
      } 
 
      _current = 0; 
      return false;
 
    } 
 
    std::string get(size_t row, size_t field) 
    { 
       return _resultSet[row][field]; 
    } 
  
    std::vector<std::string> get(size_t row) 
    { 
       return _resultSet[row]; 
    } 
 
    size_t countRows(void) 
    { 
       if (_resultSet.empty()) return 0; 
       return _resultSet.size(); 
    } 
 
    size_t countFields(void) 
    { 
       if (_resultSet[0].empty()) return 0; 
       return _resultSet[0].size(); 
    } 
 
   private: 
 
     std::vector<std::vector<std::string> > _resultSet; 
     size_t _current; 
 
}; // ResultSet

当 `SELECT` 命令发送到数据库时,它接收其结果。

为了保持简单,此类存在一些可能需要更改的缺点,这些缺点是:

  • ResultSet 只处理 `string`。
  • ResultSet 将所有数据放入一个 `std::vector` 中,它不会逐行获取,这对于大型结果集可能是一个问题。

5.4 MySql

#include "Exception.h" 
#include "ResultSet.h" 
#include "DataBase.h" 

#include <mysql.h> 

class MySql 
    /// MySql data base class 
{ 
    friend class DataBase<MySql>; 

public: 

    MySql() 
    { 
    } 

    virtual ~MySql() 
    {
    } 

    void connect(const std::string& server, const std::string& user,
        const std::string& password, const std::string& database) 
    {
        _connectionHandlerPtr = mysql_init(NULL);

        if (NULL == mysql_real_connect(_connectionHandlerPtr, server.c_str(),
            user.c_str(), password.c_str(), database.c_str(), 0, NULL, 0)) 
        {
            throw DataBaseError("Failed to connect to database: Error: " +
                std::string(mysql_error(_connectionHandlerPtr))); 
        } 
    } 

    void execute(const std::string& sql) 
    { 
        std::cout << sql << std::endl; 

        if(!(mysql_query(_connectionHandlerPtr, sql.c_str()) == 0) )
        {  
            throw DataBaseError("Failed to execute sql: Error: " +
               std::string(mysql_error(_connectionHandlerPtr)));
        } 
    }


    void populate(ResultSet& rs) 
    { 
        MYSQL_RES *result = NULL; // result of querying for all rows in table 

        // You must call mysql_store_result() or mysql_use_result() 
        // for every query that successfully retrieves data (SELECT, SHOW,
        // DESCRIBE, EXPLAIN). 
        result = mysql_use_result(_connectionHandlerPtr); 

        MYSQL_ROW row; 
        unsigned int num_fields; 
        unsigned int i; 
        num_fields = mysql_num_fields(result); 

        // get rows 
        while ((row = mysql_fetch_row(result))) 
        { 
            std::vector<std::string> myRow; 

            // get fields od row 
            for(i = 0; i < num_fields; i++) 
            { 
                if (row[i] == NULL) 
                { 
                    myRow.push_back("NULL"); }
            } 
     else
     { 
         myRow.push_back(row[i]); 
     } 
        } 

        rs.addRow(myRow);
    } 

    mysql_free_result(result); 
} 

 protected: 

     void close(void) 
     { 
         mysql_close(_connectionHandlerPtr); 
     } 

 private: 

     MYSQL* _connectionHandlerPtr; 
     // This structure represents a handle to one database connection.


}; // MySql

此类是 MySQL 实现,在其中我们使用 C API 来操作/访问数据。

我将从:**`MYSQL* _connectionHandlerPtr;`** 开始,这是数据库连接的句柄,几乎所有 MySQL 函数都使用它,请记住,您不应该尝试复制 MYSQL 结构。

在 `void connect(...)` 中,我们使用这些 API 函数:

第一个为后续调用 **`mysql_real_connect(...)`** 分配/初始化一个 MYSQL 对象,该函数尝试建立与 MySQL 数据库引擎的连接。

如果内存不足,**`mysql_init(...);`** 将返回 **`NULL`**。

如果连接不成功,`mysql_real_connect(...);` 将返回 **`NULL`**。

要获取错误描述,我们使用 `mysql_error(...)`,它返回一个 `null` 终止的 `string`,其中包含最近一次失败的 API 函数的错误消息。

在 `execute(..)` 中,我们使用这些 API 函数:

这会关闭先前打开的连接,并根据由 `mysql_init()` 或 `mysql_connect()` 自动分配的句柄,释放 `mysql` 指向的连接句柄。

好了,这就是全部内容,我们使用了 10 个 MySQL C API 函数来构建这组提供简单访问/操作 MySQL 数据库的类。

6. 接口使用

在这里,我将展示如何使用我们的类集,请参阅:

#include "MySql.h" 
 
int main() 
{ 
    try 
   {  
      ResultSet rs1, rs2; 
      DataBase<MySql> dataBase; 
 
      dataBase.connect("205.XXX.XXX.XX", "user", "pass", "db"); 
 
     // CREATE FIRST TABLE 
     dataBase << "CREATE TABLE if not exists tblTest1(test char(15) NOT NULL,
         testInt INT NULL, Constraint PK Primary Key(test))"; 

     // INSERT SOME ITENS 
     dataBase << "INSERT INTO tblTest1(test, testInt) VALUES('00', 1)" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('01')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('02')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('03')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('04')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('05')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('06')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('07')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('08')" ; 
     dataBase << "INSERT INTO tblTest1(test) VALUES('09')" ; 
 
     // CREATE SECOND TABLE 
     dataBase << "CREATE TABLE if not exists tblTest2(test char(15) NOT NULL,
         dt DATE NULL, Constraint PK Primary Key(test))"; 
  
     // INSERT SOME ITENS 
     dataBase << "INSERT INTO tblTest2(test, dt) VALUES('01', '1979/11/14')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('02')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('03')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('04')" ;
     dataBase << "INSERT INTO tblTest2(test) VALUES('05')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('06')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('07')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('08')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('09')" ; 
     dataBase << "INSERT INTO tblTest2(test) VALUES('10')" ; 
 
     // GET (one column) SAME ITENS OF tblTest1 
     dataBase << "SELECT * FROM tblTest1, tblTest2 WHERE tblTest1.test = tblTest2.test",
         rs1;  

     std::string value; 
     std::vector<std::string> row; 
  
     while(rs1.fetch(0, value)) 
     { 
         std::cout << value << std::endl; 
     } 
 
     while(rs1.fetch(row)) 
     { 
        for (size_t i = 0; i < row.size(); i++) 
        { 
           std::cout << row[i] << " | "; 
        } 
       
        std::cout << std::endl; 
     } 
 
     // GET A SPECIFIC ITEN 
     std::cout << rs1.get(0)[0] << " | " << rs1.get(0)[1] << std::endl; 
 
     // GET (one column) SAME ITENS OF tblTest2 
     dataBase << "SELECT * FROM tblTest2 WHERE test = '01'", rs2; 
 
     while(rs2.fetch(1, value)) 
     { 
         std::cout << value << std::endl; 
     } 
  
     while(rs2.fetch(row)) 
     { 
        for (size_t i = 0; i < row.size(); i++) 
        { 
           std::cout << row[i] << " | "; 
        } 
 
        std::cout << std::endl; 
     } 
 
     // GET A SPECIFIC ITEN 
     std::cout << rs2.get(0, 1) << std::endl; 
   } 
   catch (const DataBaseError& e) 
   { 
       std::cout << e.what() << std::endl; 
   } 
 
  return 0;  
}

7. 结论

如今,MySQL 是一个功能强大的开源关系数据库替代品,它提供快速的性能、高可靠性、易用性、成本效益,并且提供许多语言的访问 API,应用广泛且易于使用。

参考文献

C++ 书籍 (列表)

此处列出的 Web 链接将来可能无效。

© . All rights reserved.