ubuntu18.04下mysql数据库C语言API封装

  mysql C语言API操作数据库比较繁琐,可以将其封装起来,这样使用比较方便,下面是一种封装方式。

目录

1.连接封装

2.连接池封装

3.测试代码


1.连接封装

        将数据库连接进行封装,主要提供如下接口:

(1)连接操作

(2)更新操作(增加 /  删除 / 修改 )

(3)查询操作(查询要获取结果集,所以单独封装一个接口)

(4)获取下一条记录

(5)获取查询结果的列数(为了遍历一行中各个列时使用)

(6)获取一条记录的某个字段值

(7)设置手动提交事务(mysql默认自动提交事务)

(8)提交事务

(9)回滚事务

头文件:ConnMysql.h

#pragma once
#include <iostream>
#include <string>
#include <mysql/mysql.h>
#include <chrono>

using namespace std;

class ConnMysql {
public:
   ConnMysql();
   ~ConnMysql();
   bool Connect(const std::string &ip,
            const std::string &user,
            const std::string &pwd,
            const std::string &db,
            const unsigned short &port);
   bool Update(const std::string &sql);
   bool Query(const std::string &sql);
   bool Next();
   unsigned int GetColNum();
   std::string GetValue(const int& index);
   bool SetTransaction();
   bool Commit();
   bool Rollback(); 

private:
    void init();
    void uninit();
    void freeResult();

private:
    MYSQL *m_mysql;
    MYSQL_RES *m_result;
    MYSQL_ROW m_row = nullptr;
};

实现文件:ConnMysql.cpp

#include "ConnMysql.h"

using namespace chrono;

ConnMysql::ConnMysql() {
    init();
}

ConnMysql::~ConnMysql() {
    uninit();
}

bool ConnMysql::Connect(const std::string &ip,
            const std::string &user,
            const std::string &pwd,
            const std::string &db,
            const unsigned short &port) {
    if (!m_mysql) {
        return false;
    }
    m_mysql = mysql_real_connect(m_mysql, ip.c_str(), user.c_str(), pwd.c_str(), db.c_str(), port, NULL, 0);
    if (!m_mysql) {
        return false;
    }
    return true;    
}

bool ConnMysql::Update(const std::string &sql) {
    if (mysql_query(m_mysql, sql.c_str())) {
        return false;
    }
    return true;
}

bool ConnMysql::Query(const std::string &sql) {
    freeResult();
    if (mysql_query(m_mysql, sql.c_str())) {
        return false;
    }
    m_result = mysql_store_result(m_mysql);
    return true; 
}

bool ConnMysql::Next() {
    if (!m_result) {
        return false;
    }
    m_row = mysql_fetch_row(m_result);
    if (m_row) {
        return true;
    }
    return false;
}

unsigned int ConnMysql::GetColNum() {
    return mysql_num_fields(m_result);
}

std::string ConnMysql::GetValue(const int& index) {
    //获取结果集列数
    int rowNum = mysql_num_fields(m_result);
    if (index >= rowNum || index < 0) {
        return std::string("");
    }
    char* value = m_row[index];
    auto length = mysql_fetch_lengths(m_result)[index];
    return std::string(value, length);
}

bool ConnMysql::SetTransaction() {
    return mysql_autocommit(m_mysql, false);
}

bool ConnMysql::Commit() {
    return mysql_commit(m_mysql);
}

bool ConnMysql::Rollback() {
    return mysql_rollback(m_mysql);
}

void ConnMysql::init() {
    m_mysql = nullptr;
    m_result = nullptr;
    m_mysql = mysql_init(nullptr);
    if (m_mysql) {
        mysql_set_character_set(m_mysql, "utf8");
    }
}

void ConnMysql::uninit() {
    freeResult();
    if (m_mysql) {
        mysql_close(m_mysql);
        m_mysql = nullptr;
    }
}

void ConnMysql::freeResult() {
    if (m_result) {
        mysql_free_result(m_result);
        m_result = nullptr;
    }
}

2.连接池封装

       mysql的api通过tcp/ip协议连接数据库服务器,建立连接和断开连接需要经历三次握手和四次挥手,频繁的连接和断开会使得程序运行效率很低,可以采用连接池的方式来提高操作数据库效率,连接池的本质就是复用连接,一个连接可以重复使用,避免和服务器频繁建立连接和断开连接,从而提高程序执行效率。

头文件:ConnMysqlPool.h

#pragma once
#include <string>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "ConnMysql.h"

using namespace std;

class ConnMysqlPool {
public:
    ConnMysqlPool(const ConnMysqlPool&) = delete;
    ConnMysqlPool& operator=(const ConnMysqlPool&) = delete;
    static ConnMysqlPool& instance();
    ~ConnMysqlPool();
    std::shared_ptr<ConnMysql> GetConn();

private:
    ConnMysqlPool(const std::string &ip = "127.0.0.1",
                const unsigned short &port = 3306,
                const std::string &user = "root",
                const std::string &pwd = "mysql",
                const std::string &db = "TestDB",
                const int &min = 5,
                const int &max = 10,
                const int &idleTime = 60,
                const int &timeout = 60);
    void init();
    void addNewConn();

private:
    std::string m_ip;
    unsigned short m_port;
    std::string m_user;
    std::string m_pwd;
    std::string m_db;
    int m_min;
    int m_max;
    int m_idleTime;
    int m_timeout;
    std::mutex m_mutex;
    std::condition_variable m_cond;
    std::queue<std::shared_ptr<ConnMysql>> m_connQueue;
    std::atomic_bool m_exit;
};


实现文件:ConnMysqlPool.cpp

#include <thread>
#include <chrono>
#include "ConnMysqlPool.h"

ConnMysqlPool::ConnMysqlPool(const std::string &ip,
                const unsigned short &port,
                const std::string &user,
                const std::string &pwd,
                const std::string &db,
                const int &min,
                const int &max,
                const int &idleTime,
                const int &timeout) : 
m_ip(ip),
m_port(port),
m_user(user),
m_pwd(pwd),
m_db(db),
m_min(min),
m_max(max),
m_idleTime(idleTime),
m_timeout(timeout),
m_exit(false)   
{
    init();
}

ConnMysqlPool::~ConnMysqlPool() {
    m_exit.store(false);
    m_cond.notify_all();
    while (!m_connQueue.empty()) {
        m_connQueue.pop();
    }
}


ConnMysqlPool& ConnMysqlPool::instance() {
    static ConnMysqlPool s_instance;
    return s_instance;
}

std::shared_ptr<ConnMysql> ConnMysqlPool::GetConn() {
    std::unique_lock<std::mutex> lock(m_mutex);
    while (m_connQueue.empty()) {
        if (m_connQueue.empty()) {
            if (cv_status::timeout == m_cond.wait_for(lock, chrono::milliseconds(m_timeout))) {
                if (m_connQueue.empty()) {
                    continue;
                }
            }
        }
    }
    auto mysql = m_connQueue.front();
    m_connQueue.pop();
    m_cond.notify_all();
    return mysql;
}

void ConnMysqlPool::init() {
    for (int i = 0; i < m_min; i++) {
        addNewConn();
    }
    std::thread producer([this](){
        while (m_exit) {
            std::unique_lock<std::mutex> lock(m_mutex);
            //此处使用while,养成条件变量判断使用while而不是使用if
            while (m_connQueue.size() >= m_min) {
                m_cond.wait(lock);
            }
            addNewConn();
            m_cond.notify_all();
        }
        
    });
    producer.detach();
}

void ConnMysqlPool::addNewConn() {
    std::shared_ptr<ConnMysql> mysql(new ConnMysql, [this](ConnMysql* conn) {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_connQueue.push(std::shared_ptr<ConnMysql>(conn));
    });
    mysql->Connect(m_ip, m_user, m_pwd, m_db, m_port);
    m_connQueue.push(mysql);
}

3.测试代码

#include <iostream>
#include "ConnMysql.h"
#include "ConnMysqlPool.h"

using namespace std;

//mysql8.0连接数据库需要对数据进行加密,调用api库时需要额外的加密库
//多个线程使用同样的用户名和密码同时去连接数据库,数据库会拒绝一些连接,解决办法是在同时连接前先单独连接一次
void testConnMysql() {
    ConnMysql mysql;
    mysql.SetTransaction();
    mysql.Connect("127.0.0.1", "root", "mysql", "TestDB", 3306);
    std::string sql = "insert into employee(name, address, age, position, remark) values('张三', '陕西', 21, 'higher', '路人甲')";
    if (mysql.Query(sql)) {
        mysql.Commit();
        std::cout << "insert ok !" << std::endl;
    }
    else {
        mysql.Rollback();
        std::cout << "insert error !" << std::endl;
    }
    sql = "select * from employee";
    if (mysql.Query(sql)) {
        std::cout << "select ok !" << std::endl;
        auto colNum = mysql.GetColNum();
        while (mysql.Next()) {
            for(int i = 0; i < colNum; i++) {
                std::cout << mysql.GetValue(i) << " ";
            }
            std::cout << std::endl;
        }
    }
    else {
        std::cout << "select error !" << std::endl;
    }

}

void testConnMysqlPool() {
    auto mysqlConn = ConnMysqlPool::instance().GetConn();
    mysqlConn->SetTransaction();
    std::string sql = "insert into employee(name, address, age, position, remark) values('李四', '北京', 22, 'higher', '路人乙')";
    if (mysqlConn->Query(sql)) {
        mysqlConn->Commit();
        std::cout << "insert ok !" << std::endl;
    }
    else {
        mysqlConn->Rollback();
        std::cout << "insert error !" << std::endl;
    }
    sql = "select * from employee";
    if (mysqlConn->Query(sql)) {
        std::cout << "select ok !" << std::endl;
        auto colNum = mysqlConn->GetColNum();
        while (mysqlConn->Next()) {
            for(int i = 0; i < colNum; i++) {
                std::cout << mysqlConn->GetValue(i) << " ";
            }
            std::cout << std::endl;
        }
    }
    else {
        std::cout << "select error !" << std::endl;
    }
}

int main(int argc, char* argv[]) {
    std::cout << "test1 ..." << std::endl;
    testConnMysql();
    std::cout << "test2 ..." << std::endl;
    testConnMysqlPool();
    std::cout << "end" << std::endl;
    return 0;
}

Makefile

app: useMysqlConnPool

useMysqlConnPool: usemysqlconnpool.cpp ConnMysql.cpp ConnMysqlPool.cpp  
	g++ -std=c++11 -g $^ -o useMysqlConnPool -lpthread -lmysqlclient 

clean:
	-rm useMysqlConnPool -f

运行结果如下:

 数据库数据:

猜你喜欢

转载自blog.csdn.net/hsy12342611/article/details/128522101