一 、前言
在C++項目中經常要使用Mysql數據庫,在QT項目中使用Mysql還需要 Mysql驅動,但是在QT4.7,4.8版本中,mysql驅動需要自己編譯,在編譯過程中會遇到很多問題,需要花很多時間來解決,所以我們要想別的辦法;一是,不直接使用mysql驅動來操作mysql數據庫,可以使用odbc的方式來操作數據庫,並且使用ODBC操作數據之前需要配置ODBC源;二是,可以繞開QT的數據庫框架,直接使用mysql原生的庫來操作mysql;所以我們基於原生的庫進行了二次封裝。
二、源代碼
頭文件.h
#ifndef DATABASE_MYSQL_H
#define DATABASE_MYSQL_H
#include <string>
#include <vector>
#include "database_mysql_global.h"
class database_mysql_t;
//批量插入數據
class DATABASE_MYSQL_EXPORT batch_insert_adapter_t
{
public:
batch_insert_adapter_t(database_mysql_t *database, int buffer_size = 1000000, int flush_row = 50000);
~batch_insert_adapter_t();
void begin_add_field(const char * csz_table); //開始插入字段
void add_field(const char *csz_field); //添加字段
void end_add_field(); //結束插入字段
void begin_add_row(); //開始插入數值
void add_value(char value);
void add_value(int value);
void add_value(unsigned int value);
void add_value(long value);
void add_value(unsigned long value);
void add_value(float value);
void add_value(double value);
void add_value(std::string value);
void add_value(const char * value);
void add_value(long long value);
void add_value(unsigned long long value);
void add_value(void * value, int n_length);
bool end_add_row(); //結束添加值
bool flush(); //將數據插入數據庫
int get_sum_row(); //獲取總的行數
private:
std::string m_str_table; //表格名稱
std::vector<std::string> m_v_field_names; //需要插入的字段名稱
int m_n_field; //字段個數
int m_n_field_added; //已經添加的字段數
database_mysql_t *m_database;
int m_buffer_size;
int m_flush_row;
char *m_data_buffer; //數據緩存
char * m_p_data; //數據緩存當前的位置
char * m_p_data_start; //數據開始位置
char * m_p_row_start; //行的起始地址
int m_n_row_num; //行數
int m_n_row_max_length; //行長度
int m_n_sum_row;
};
//MYSQL返回的行定義
typedef char** row_t;
//返回結果集
class DATABASE_MYSQL_EXPORT res_t //爲select的返回結果
{
friend class database_mysql_t;
public:
int row_num(); //獲取結果集的行數
int field_num(); //獲取字段個數
unsigned long * field_lengths();//獲取當前行的各個字段長度
row_t get_next_row(); //獲取下一行數據
row_t get_row(int n_offset); //獲取指定偏移的數據
row_t operator[](int n_offset); //獲取第幾行
inline void erase(); //清空本結果集
res_t();
res_t(void *res);
res_t(res_t& res); //拷貝構造函數
res_t& operator=(res_t& res); //賦值
operator bool(void); //判斷結果集是否返回成功
~res_t();
private:
void * m_data; //數據
};
//數據庫適配類
class DATABASE_MYSQL_EXPORT database_mysql_t
{
public:
database_mysql_t(void);
virtual ~database_mysql_t(void);
bool connet(const char *csz_host, const char *csz_user, const char *csz_password, const char *csz_db=0, const unsigned int un_port = 3306); //連接數據庫
void dis_connect(); //斷開連接
bool use_db(const char *csz_db); //切換數據庫
bool set_char_set(const char * csz_char_set); //設置字符集
bool has_db(const char * csz_db); //數據庫是否存在
bool create_db(const char * csz_db); //創建數據庫,輸入爲數據庫的名稱
bool drop_db(const char * csz_db); //刪除數據庫,輸入爲數據庫的名稱
bool has_table(const char * csz_table); //該表格是否存在
bool create_table(const char *csz_sql); //創建表,輸入爲創建表的sql語句
bool drop_table(const char * csz_talbe); //刪除表,輸入爲表的名稱
res_t select(const char * csz_sql); //查詢
int insert(const char * csz_sql, int n_length = 0); //插入
int update(const char * csz_sql, int n_length = 0); //更新
int remove(const char * csz_sql); //刪除
int get_affected_rows(); //獲取受到影響的行
bool autocommit(bool bflag); //不允許自動事務處理
bool commit(); //執行事務
bool rollback(); //事務回滾
bool query(const char * csz_data, int n_length = 0); //查詢,返回受影響的行數
const char * get_error(); //返回數據庫操作的錯誤信息
unsigned int get_errno(); //返回數據庫操作錯誤代碼 非錯誤返回0
private:
void * m_mysql; //mysql連接句柄
};
#endif // DATABASE_MYSQL_H
源文件.cpp
#include "stdafx.h"
#include "database_mysql.h"
#define __LCC__
#include <mysql.h>
//批量插入適配器
batch_insert_adapter_t::batch_insert_adapter_t( database_mysql_t *database, int buffer_size/* = 1000000*/, int flush_row/* = 50000*/)
: m_database(database), m_n_field(0), m_n_field_added(0), m_p_data(0), m_p_data_start(0),m_n_row_num(0), m_n_row_max_length(0),m_p_row_start(0),m_n_sum_row(0),m_buffer_size(buffer_size),m_flush_row(flush_row)
{
m_data_buffer = new char[buffer_size];
memset(m_data_buffer, 0, m_buffer_size);
}
batch_insert_adapter_t::~batch_insert_adapter_t()
{
delete[] m_data_buffer;
}
void batch_insert_adapter_t::begin_add_field(const char * csz_table)
{
m_str_table = csz_table;
m_v_field_names.clear(); //將以往的字段名刪除
m_p_data = m_data_buffer;
m_n_field = 0;
m_n_field_added = 0;
m_p_data_start = 0;
m_n_row_num = 0;
m_n_row_max_length = 0;
m_n_sum_row = 0;
}
void batch_insert_adapter_t::add_field( const char *csz_field )
{
m_v_field_names.push_back(csz_field);
}
void batch_insert_adapter_t::end_add_field()
{
int pos = sprintf(m_p_data, "insert into %s(", m_str_table.c_str());
m_p_data += pos;
for (std::vector<std::string>::iterator iter = m_v_field_names.begin(), iter_end = m_v_field_names.end(); iter != iter_end; ++iter)
{
pos = sprintf(m_p_data, "%s,", (*iter).c_str());
m_p_data += pos;
}
--m_p_data;
pos = sprintf(m_p_data, ") values ");
m_p_data += pos;
m_p_data_start = m_p_data; //記錄數據開始位置
m_n_field = (int)m_v_field_names.size(); //獲取字段個數
}
void batch_insert_adapter_t::begin_add_row()
{
m_p_row_start = m_p_data; //行的起始地址
m_n_field_added = 0;
int pos = sprintf(m_p_data, "(");
m_p_data += pos;
}
void batch_insert_adapter_t::add_value(char value )
{
int pos = sprintf(m_p_data, "\'%c\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(int value )
{
int pos = sprintf(m_p_data, "\'%d\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(unsigned int value )
{
int pos = sprintf(m_p_data, "\'%u\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(long value )
{
int pos = sprintf(m_p_data, "\'%d\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(unsigned long value )
{
int pos = sprintf(m_p_data, "\'%u\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(float value )
{
int pos = sprintf(m_p_data, "\'%f\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(double value )
{
int pos = sprintf(m_p_data, "\'%f\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(std::string value )
{
int pos = sprintf(m_p_data, "\'%s\',", value.c_str());
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(const char * value )
{
int pos = sprintf(m_p_data, "\'%s\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(long long value)
{
int pos = sprintf(m_p_data, "\'%lld\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(unsigned long long value)
{
int pos = sprintf(m_p_data, "\'%llu\',", value);
m_p_data += pos;
++m_n_field_added;
}
void batch_insert_adapter_t::add_value(void * value, int n_length )
{
*m_p_data = '\''; //用單引號將要插入的值引住
++m_p_data;
memcpy(m_p_data, value, n_length);
m_p_data += n_length;
*m_p_data = '\'';
++m_p_data;
*m_p_data = ','; //在字段後面添加','
++m_p_data;
++m_n_field_added;
}
bool batch_insert_adapter_t::end_add_row()
{
if (m_n_field_added != m_n_field)
{
//log_t::instance().log("字段值添加錯誤");
return false;
}
*(m_p_data-1) = ')'; //最後一個字段值取出','添加')'
*m_p_data = ','; //添加行間隔','
++m_p_data;
++m_n_row_num; //行數
int n_row_length = (int)(m_p_data - m_p_row_start);
m_n_row_max_length = (m_n_row_max_length > n_row_length) ? m_n_row_max_length : n_row_length;
if (m_n_row_num > m_flush_row || m_p_data - m_data_buffer + m_n_row_max_length + 100 > m_buffer_size)
{
return flush();
}
else
return true;
}
bool batch_insert_adapter_t::flush()
{
if (m_n_row_num != 0)
{
*(m_p_data-1) = '\0';
int n_length = (int)(m_p_data - m_data_buffer);
m_p_data = m_p_data_start;
if (!m_database->query(m_data_buffer, n_length))
{
const char *p = m_database->get_error();
return false;
}
int num = m_database->get_affected_rows();
if (num != m_n_row_num)
{
return false;
}
m_n_sum_row += num;
m_n_row_num = 0;
}
return true;
}
int batch_insert_adapter_t::get_sum_row() //獲取總的行數
{
return m_n_sum_row;
}
//數據庫接口
database_mysql_t::database_mysql_t(void) : m_mysql(0)
{
}
database_mysql_t::~database_mysql_t(void)
{
if (m_mysql != 0)
{
dis_connect();
}
}
bool database_mysql_t::connet( const char *csz_host, const char *csz_user, const char *csz_password, const char *csz_db, const unsigned int un_port /*= 3306*/ )
{
m_mysql = mysql_init((MYSQL *)m_mysql);
if (m_mysql == 0)
{
return false;
}
if (mysql_real_connect((MYSQL *)m_mysql, csz_host, csz_user, csz_password, csz_db, un_port, 0, 0)==0)
{
return false;
}
return true;
}
void database_mysql_t::dis_connect()
{
mysql_close((MYSQL *)m_mysql);
m_mysql = 0;
}
bool database_mysql_t::use_db( const char *csz_db )
{
char sz_temp[50] = {0};
sprintf(sz_temp, "use %s", csz_db);
return query(sz_temp);
}
bool database_mysql_t::set_char_set( const char * csz_char_set )
{
char sz_temp[50] = {0};
sprintf(sz_temp, "set names %s", csz_char_set);
return query(sz_temp);
}
res_t database_mysql_t::select( const char * csz_sql )
{
if (!query(csz_sql))
{
return 0;
}
res_t res(mysql_store_result((MYSQL *)m_mysql));
return res;
}
int database_mysql_t::insert( const char * csz_sql, int n_length /*= 0*/ )
{
if (!query(csz_sql, n_length))
{
return -1;
}
return get_affected_rows();
}
int database_mysql_t::update( const char * csz_sql, int n_length /*= 0*/ )
{
if (!query(csz_sql, n_length))
{
return -1;
}
return get_affected_rows();
}
int database_mysql_t::remove( const char * csz_sql )
{
if (!query(csz_sql))
{
return -1;
}
return get_affected_rows();
}
bool database_mysql_t::query( const char * csz_data, int n_length /*= 0*/ )
{
if (n_length == 0)
{
n_length = (int)strlen(csz_data);
}
return !(mysql_real_query((MYSQL *)m_mysql, csz_data, n_length)); // 查詢成功返回0
}
int database_mysql_t::get_affected_rows()
{
return static_cast<int>(mysql_affected_rows((MYSQL *)m_mysql));
}
bool database_mysql_t::autocommit( bool bflag )
{
return !(mysql_autocommit((MYSQL *)m_mysql, bflag));
}
bool database_mysql_t::commit()
{
return !(mysql_commit((MYSQL *)m_mysql));
}
bool database_mysql_t::rollback()
{
return !(mysql_rollback((MYSQL *)m_mysql));
}
//創建數據庫
bool database_mysql_t::create_db( const char * csz_db )
{
char sz_temp[50] = {0};
int n_len = sprintf(sz_temp, "create database %s", csz_db);
return query(sz_temp, n_len);
}
bool database_mysql_t::drop_db( const char * csz_db )
{
char sz_temp[50] = {0};
int n_len = sprintf(sz_temp, "drop database %s", csz_db);
return query(sz_temp, n_len);
}
bool database_mysql_t::create_table( const char *csz_sql )
{
return query(csz_sql);
}
bool database_mysql_t::drop_table( const char * csz_talbe )
{
char sz_temp[50] = {0};
int n_len = sprintf(sz_temp, "drop table %s", csz_talbe);
return query(sz_temp, n_len);
}
const char * database_mysql_t::get_error()
{
return mysql_error((MYSQL *)m_mysql);
}
unsigned int database_mysql_t::get_errno()
{
return mysql_errno((MYSQL *)m_mysql);
}
bool database_mysql_t::has_db( const char * csz_db )
{
res_t res = select("show databases");
if (!res)
{
return false;
}
else
{
row_t row = 0;
int num = res.row_num();
for (int i=0; i<num; ++i)
{
row = res.get_next_row();
if (!strcmp(csz_db, row[0]))
{
return true;
}
}
return false;
}
}
bool database_mysql_t::has_table( const char * csz_table )
{
res_t res = select("show tables");
if (!res)
{
return false;
}
else
{
row_t row = 0;
int num = res.row_num();
for (int i=0; i<num; ++i)
{
row = res.get_next_row();
if (!strcmp(csz_table, row[0]))
{
return true;
}
}
return false;
}
}
//查詢結果集
res_t::res_t() : m_data(0)
{
}
res_t::res_t( res_t& res )
{
m_data = res.m_data;
res.m_data = 0;
}
res_t::res_t( void *res )
{
m_data = res;
}
res_t::~res_t()
{
erase();
}
res_t& res_t::operator=( res_t& res)
{
erase();
m_data = res.m_data;
res.m_data = 0;
return *this;
}
int res_t::row_num()
{
return static_cast<int>(mysql_num_rows((MYSQL_RES *)m_data));
}
int res_t::field_num()
{
return static_cast<int>(mysql_num_fields((MYSQL_RES *)m_data));
}
unsigned long * res_t::field_lengths()
{
return mysql_fetch_lengths((MYSQL_RES *)m_data);
}
row_t res_t::get_next_row()
{
return mysql_fetch_row((MYSQL_RES *)m_data);
}
row_t res_t::get_row( int n_offset )
{
mysql_data_seek((MYSQL_RES *)m_data, n_offset);
return get_next_row();
}
row_t res_t::operator[]( int n_offset )
{
return get_row(n_offset);
}
res_t::operator bool( void )
{
return m_data ? true : false;
}
void res_t::erase()
{
if (m_data)
{
mysql_free_result((MYSQL_RES *)m_data);
m_data = 0;
}
}
三、使用示例
// main.cpp : 定義控制檯應用程序的入口點。
#include "stdafx.h"
#include <iostream>
#include <database_mysql.h>
int _tmain(int argc, _TCHAR* argv[])
{
database_mysql_t m_MysqlDb;
if (!m_MysqlDb.connet("127.0.0.1", "root", "123456", "epidemic_deduction"))
{
return 0;
}
m_MysqlDb.set_char_set("utf8");
std::ostringstream sql;
sql << "insert into frame_list(id,num) values(\""
<< 1000001<<"\",\"" <<10<<"\")";
if (m_MysqlDb.insert(sql.str().c_str())==-1)
{
std::cout << database.get_error();
}
m_MysqlDb.dis_connect();
return 0;
}
批量插入示例
void BatchWriteExpData(std::vector<std::string> expDatas)
{
std::cout << "start batch insert expdata[" << expDatas.size() << "]" << std::endl;
batch_insert_adapter_t* adapter = new batch_insert_adapter_t(&m_MysqlDb);
adapter->begin_add_field("exp_data_list"); //插入信息
adapter->add_field("id");
adapter->add_field("step_id");
adapter->add_field("exp_id");
adapter->add_field("src_id");
adapter->add_field("tgt_id");
adapter->add_field("behaior_type");
adapter->add_field("time");
adapter->end_add_field();
for (std::vector<std::string>::iterator it = expDatas.begin(); it != expDatas.end(); ++it)
{
std::vector<std::string> dataList = vStringSplit(*it);
adapter->begin_add_row();
adapter->add_value(dataList.at(0));
adapter->add_value(dataList.at(1));
adapter->add_value(dataList.at(2));
adapter->add_value(dataList.at(3));
adapter->add_value(dataList.at(4));
adapter->add_value(dataList.at(5));
adapter->add_value(dataList.at(6));
adapter->end_add_row();
}
adapter->flush();
delete adapter;
std::cout << "Batch insert success" << std::endl;
}