数据库_之API
续上一篇的数据库,再针对数据库的函数做一些简单的分析.
本文参考链接:
MySQL C API 使用(基本函数)
Mysql C API函数详解
MySQL的常用API
一个常用的程序调用MySQL数据库的时候通常都会调用以下API,下面来逐个分析.
-
mysql_init()
//函数原型 MYSQL *STDCALL mysql_init(MYSQL *mysql);
这个
API
主要是用来分配或初始化一个MySQL
对象,用来连接MySQL服务端;
返回值是MYSQL *
;
参数MYSQL *mysql
是指定一个MYSQL
对象. -
mysql_options()
//函数原型 int STDCALL mysql_options(MYSQL *mysql, enum mysql_option option, const void *arg);
此
API
可用于设置额外的连接选项,并影响连接的行为.本函数可多次调用来设置数个选项;
参数MYSQL *mysql
是指定一个MYSQL
对象;
参数enum mysql_option option
是供调用者选择填写的连接选项;
参数const void *arg
是结合上一个参数填写的连接选项的值.关于连接选项的填写,可以参考 mysql_options()函数
-
mysql_real_connect()
//函数原型 MYSQL *STDCALL mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);
这个
API
主要是用来分配或初始化一个MySQL
对象,用来连接MySQL
服务端.
参数MYSQL *mysql
是指定一个MYSQL
对象,此处为mysql_init
函数返回的指针;
参数const char *host
指定主机地址;
参数const char *user
指定mysql
用户名;
参数const char *passwd
输入该用户名的密码;
参数const char *db
指定数据库,当该值为空时链接到默认的数据库;
参数unsigned int port
指定端口;
参数const char *unix_socket
指定使用unix链接方式,为空时表明不适用socket
或管道机制;
参数unsigned long clientflag
通常设置为0. -
mysql_query()
//函数原型 int STDCALL mysql_query(MYSQL *mysql, const char *q);
此
API
是最常用的,增删查改都使用到它,query
英文意思是查询,但这里表示执行
.
参数MYSQL *mysql
是指定一个MYSQL
对象.
参数const char *
输入一个SQL
语句. -
mysql_store_result()
//函数原型 MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql);
获取结果集,通常是搭配
mysql_query
这个API
来使用,查询上一条SQL
语句返回的结果.
参数MYSQL *mysql
是指定一个MYSQL
对象
返回值MYSQL_RES
是一个句柄,可以使用mysql_fetch_row
取出这个结果集里头有多少行数据. -
mysql_use_result()
//函数原型 MYSQL_RES *STDCALL mysql_use_result(MYSQL *mysql);
这个
API
是在调用一个可以产生结果集的命令之后,搭配本函数使用获取该结果集;
参数MYSQL *mysql
是指定一个MYSQL
对象. -
mysql_fetch_row()
//函数原型 MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
结合
mysql_store_result
这个API
来使用,获取每一行的内容.
参数MYSQL_RES *result
是通过mysql_use_result
获取到的返回值. -
mysql_free_result()
//函数原型 void STDCALL mysql_free_result(MYSQL_RES *result);
操作完结果集之后.一定要调用本函数来释放内存;
参数MYSQL_RES *result
是通过mysql_use_result
获取到的返回值. -
mysql_error()
//函数原型 const char *STDCALL mysql_error(MYSQL *mysql);
函数返回值是
const char*
类型,主要是返回最近调用mysql
函数发生了哪些错误信息
参数MYSQL *mysql
是指定一个MYSQL
对象 -
mysql_close()
//函数原型 void STDCALL mysql_close(MYSQL *sock);
mysql_close()
是每个调用数据库连接的程序在最后阶段都必须要调用的,
很明显它的功能是用来关闭一个MySQL服务端的连接.
参数MYSQL *mysql
是指定一个MYSQL
对象
MySQL的API应用
下面使用一个比较实际的例子,用这些API
写一个简单的单机版数据库管理软件.
本地数据库新建好两个表并存入部分初始信息:
- 先定义好包含文件:
define.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <mysql.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <assert.h>
#pragma comment(lib,"libmysql.lib")
- 定义一个管理类:
CManager
提供主菜单的显示,连接数据库和打印错误的功能.
CManager.h
#pragma once
#include "define.h"
#include "CWorker.h"
#include "CAdmin.h"
class CManager
{
MYSQL m_sql;
CWorker m_work;
CAdmin m_admin;
int Menu();
bool ConnectDB();
public:
int Main();
static void PrintError(std::string sPromp)
{
auto & sql = getInstance().m_sql;
std::cout << sPromp << "(" << mysql_errno(&sql) << "):"
<< mysql_error(&sql) << std::endl;
system("pause");
}
static CManager& getInstance()
{
static CManager m;
return m;
}
CManager() : m_admin(m_sql),m_work(m_sql)
{
}
};
CManager.cpp
#include "CManager.h"
using namespace std;
int CManager::Menu()
{
system("cls");
const char* sPrior = m_admin.GetPrior() ? "普通" : "高级";
cout << "\n\n\t\t当前用户:" << m_admin.GetUser() << "【" << sPrior << "】" << endl;
puts("\t\t********************************");
puts("\t\t*\t1、浏览所有信息 *");
puts("\t\t*\t2、添加信息 *");
puts("\t\t*\t3、删除信息 *");
puts("\t\t*\t4、修改信息 *");
puts("\t\t*\t5、查找信息 *");
if (m_admin.GetPrior())
puts("\t\t*\t6、密码修改 *");
else
puts("\t\t*\t6、管理账号 *");
puts("\t\t*\t0、退出 *");
puts("\t\t********************************");
cout << "\t\t请选择:";
int i = 0;
cin >> i;
switch (i)
{
case 1:
m_work.Browse();
//m_work.Print();
break;
case 2:
m_work.Input();
break;
case 3:
m_work.Delete();
break;
case 4:
m_work.Modify();
break;
case 5:
m_work.Find();
break;
case 6:
m_admin.Main();
}
return i;
}
bool CManager::ConnectDB()
{
if (!mysql_init(&m_sql))
{
cout << "数据库初始化失败:" << mysql_error(&m_sql) << endl;
return false;
}
if (!mysql_real_connect(&m_sql, "localhost", "root", "123456", "worker", 3306, nullptr, 0))
{
cout << "连接数据库失败(" << mysql_errno(&m_sql) << "):" << mysql_error(&m_sql) << endl;
return false;
}
mysql_query(&m_sql, "set names \'GBK\'");
return true;
}
int CManager::Main()
{
if (!ConnectDB())
return -1;
if (!m_admin.Login())
return -1;
while (Menu())
;
return 0;
}
关于C++的单例模式,可参考学习: C++中的单例模式
- 再定义一个账户管理类:
CAdmin
功能主要包括账户管理的选项,账户的增删改查等操作.
CAdmin.h
#pragma once
#include "define.h"
class CAdmin
{
MYSQL &m_sql;
int m_nPrior{0};
std::string m_sName, m_sPass;
MYSQL_ROW FindName(const char* sName);
bool Check(const char* sName, const char* sPass);
void PrintRow(MYSQL_ROW row);
void Print()
{
Select("SELECT *FROM t_admin");
}
void InsertData(const std::string& sName, const std::string& sPass, int nPrior);
void DeleteData(const std::string& sName);
void Input();
void Delete();
void ChangePass();
void ChangePass(std::string sNew);
int Menu();
void Select(const char* sSQL);
public:
int Main();
bool Login();
CAdmin(MYSQL &sql) :m_sql(sql)
{
}
int GetPrior() const
{
return m_nPrior;
}
const char* GetUser() const
{
return m_sName.c_str();
}
};
CAdmin.cpp
#include "CAdmin.h"
#include "CManager.h"
using namespace std;
MYSQL_ROW CAdmin::FindName(const char* sName)
{
stringstream ss;
ss << "SELECT * FROM t_admin WHERE f_name = '" << sName <<"'";
int n = mysql_query(&m_sql, ss.str().c_str());
if (n)
{
CManager::PrintError("查找账号时发生错误"+ ss.str());
return nullptr;
}
auto res = mysql_store_result(&m_sql);
if (!res)
return nullptr;
return mysql_fetch_row(res);
}
bool CAdmin::Check(const char* sName, const char* sPass)
{
string sSQL = "SELECT* FROM t_admin WHERE f_name = '";
sSQL += sName;
sSQL += "' AND binary f_pass = '";
sSQL += sPass;
sSQL += "'";
int n = mysql_query(&m_sql, sSQL.c_str());
if (n)
{
cout << "登录失败,数据库查询错误:" << mysql_error(&m_sql);
return false;
}
auto res = mysql_store_result(&m_sql);
if (res && mysql_num_rows(res) > 0)
{
auto row = mysql_fetch_row(res);
m_sName =*row++;
m_sPass = *row++;
m_nPrior = atoi(*row);
return true;
}
cout << "你输入的账号或密码错误!" << endl;
return false;
}
void CAdmin::PrintRow(MYSQL_ROW row)
{
assert(row);
const char* str = *row++;
if (!str)
str = "无";
cout << str << '\t';
str = *row++;
if (!str)
str = "无";
cout << str << '\t';
str = *row++;
if (str)
str = atoi(str) ? "普通" : "高级";
else
str = "无";
cout <<str<< endl;
}
void CAdmin::Select(const char* sSQL)
{
int n = mysql_query(&m_sql, sSQL);
if (n)
{
CManager::PrintError("打印数据时发生错误:" + string(sSQL));
return;
}
auto res = mysql_store_result(&m_sql);
if (!res)
{
CManager::PrintError("打印数据时获取结果集错误:" + string(sSQL));
return;
}
system("cls");
const int ws[] = { 6,16,12,0 };
int i = -1;
cout <<"账号"<<'\t'<<"密码" <<'\t'<<"权限" << endl;
//puts("工号\t姓名\t工资\t入职日期");
MYSQL_ROW row;
int nCol = mysql_num_fields(res);
while (row = mysql_fetch_row(res))
PrintRow(row);
cout << "总共有 " << mysql_num_rows(res) << " 条记录" << endl;
system("pause");
}
void CAdmin::InsertData(const std::string& sName, const std::string& sPass, int nPrior)
{
char sSQL[256];
sprintf(sSQL, "INSERT INTO t_admin VALUES('%s','%s',%d)", sName.c_str(),
sPass.c_str(), nPrior);
if (mysql_query(&m_sql, sSQL))
CManager::PrintError("插入账号信息时出错");
else
Print();
}
void CAdmin::DeleteData(const std::string& sName)
{
stringstream ss;
ss << "DELETE FROM t_admin WHERE f_name='" << sName << "'";
if (mysql_query(&m_sql, ss.str().c_str()))
CManager::PrintError("删除账号信息时出错"+ss.str());
else
Print();
}
void CAdmin::Input()
{
string sName,sPass;
int nPrior;
cout << "请输入新的账号:";
cin >> sName;
auto row = FindName(sName.c_str());
if (row)
{
cout << "你输入的账号已存在:";
PrintRow(row);
system("pause");
return;
}
cout << "请输入密码和权限(0、高级 1、普通):";
cin >>sPass >> nPrior;
InsertData(sName, sPass, nPrior);
}
void CAdmin::Delete()
{
string sName, sPass;
int nPrior;
cout << "请输入要删除的账号:";
cin >> sName;
auto row = FindName(sName.c_str());
if (!row)
{
puts("你输入的账号不存在!");
system("pause");
return;
}
PrintRow(row);
printf("你确定要删除这条记录吗?[y/n]");
char c;
cin >> c;
DeleteData(sName);
}
void CAdmin::ChangePass()
{
string sOld, sNew, sConf;//confirm
cout << "请输入旧密码:";
cin >> sOld;
if (sOld != m_sPass)
{
cout << "你输入的旧密码不正确!"<<endl;
system("pause");
return;
}
cout << "请输入新密码:";
cin >> sNew;
cout << "请再次输入新密码:";
cin >> sConf;
if (sNew != sConf)
{
cout << "两次输入的新密码不一致!" << endl;
system("pause");
return;
}
if (sNew == m_sPass)
{
cout << "新密码和旧密码不能相同!" << endl;
system("pause");
return;
}
ChangePass(sNew);
}
void CAdmin::ChangePass(std::string sNew)
{
char sSQL[256];
sprintf(sSQL, "UPDATE t_admin SET f_pass='%s' WHERE f_name='%s'", sNew.c_str(),
m_sName.c_str());
int n = mysql_query(&m_sql, sSQL);
if (n)
{
CManager::PrintError("修改密码时出错!"+string(sSQL));
return;
}
size_t nRows = mysql_affected_rows(&m_sql);
if (nRows > 0)
{
m_sPass = sNew;
cout << "修改密码成功,总共更新了" << nRows << "条记录" << endl;
}
else
cout << "修改密码失败!" << endl;
system("pause");
}
int CAdmin::Menu()
{
system("cls");
puts("\t\t********************************");
puts("\t\t*\t1、浏览账号 *");
puts("\t\t*\t2、添加账号 *");
puts("\t\t*\t3、删除账号 *");
puts("\t\t*\t4、修改密码 *");
puts("\t\t*\t0、返回主菜单 *");
puts("\t\t********************************");
cout << "\t\t请选择:";
int n;
cin >> n;
switch (n)
{
case 1:
Print();
break;
case 2:
Input();
break;
case 3:
Delete();
break;
case 4:
ChangePass();
break;
}
return n;
}
int CAdmin::Main()
{
if (m_nPrior)
ChangePass();
else
while (Menu())
;
return 0;
}
bool CAdmin::Login()
{
string sName, sPass;
cout << "请输入账号和密码:" << endl;
cin >> sName >> sPass;
return Check(sName.c_str(), sPass.c_str());
}
- 以及定义具体的工作类:
CWorker
操作数据的增删改查.
CWorker.h
#pragma once
#include "define.h"
#include "CSort.h"
#include "CFind.h"
class CWorker
{
MYSQL& m_sql;
CSort m_sort{this};
CFind m_find{this};
static void PrintField(MYSQL_ROW row, int nWidth);
static const int m_ws[] ;
void ModifyData(int nNumb);
void Print();
public:
static void PrintRow(MYSQL_ROW row, int nCol);
void Find()
{
while (m_find.Menu())
;
}
void Browse()
{
while (m_sort.Menu())
;
}
void Select(const char* sSQL);
MYSQL_ROW FindNumb(int nNumb);
bool DeleteNumb(int nNumb);
void Delete();
void Modify();
void Input();
CWorker(MYSQL& sql) :m_sql(sql)
{
}
};
CWorker.cpp
#include "CWorker.h"
#include "CManager.h"
using namespace std;
const int CWorker::m_ws[] = {6, 16, 12, 0};
//void CWorker::Print()
//{
// system("cls");
// int n = mysql_query(&m_sql,"SELECT * FROM t_people");
// if (n)
// {
// CManager::PrintError("打印数据时发生错误:");
// return;
// }
// auto res = mysql_store_result(&m_sql);
// if (!res)
// {
// CManager::PrintError("打印数据时获取结果集错误:");
// return;
// }
// puts("工号\t姓名\t工资\t入职日期");
// MYSQL_ROW row;
// int nCol = mysql_num_fields(res);
// while (row = mysql_fetch_row(res))
// {
// n = nCol;
// while (n--)
// {
// auto str = *row++;
// if (str)
// cout << str << "\t";
// else
// cout << "无\t";
// }
// putchar(10);
// }
// cout << "总共有 " << mysql_num_rows(res) << " 条记录" << endl;;
// system("pause");
//}
void CWorker::Print()
{
Select("SELECT *FROM t_people");
}
void CWorker::PrintField(MYSQL_ROW row, int nWidth)
{
auto str = *row;
cout << setiosflags(ios::left) << setw(nWidth);
if (str)
cout << str;
else
cout << "无";
}
void CWorker::PrintRow(MYSQL_ROW row, int nCol)
{
int i = -1;
while (++i < nCol)
PrintField(row++, m_ws[i]);
cout << endl;
}
MYSQL_ROW CWorker::FindNumb(int nNumb)
{
stringstream ss;
ss << "SELECT * FROM t_people WHERE f_numb = " << nNumb;
int n = mysql_query(&m_sql,ss.str().c_str());
if (n)
{
CManager::PrintError("查找工号时发生错误");
return nullptr;
}
auto res = mysql_store_result(&m_sql);
if (!res)
return nullptr;
return mysql_fetch_row(res);
}
bool CWorker::DeleteNumb(int nNumb)
{
stringstream ss;
ss << "DELETE FROM t_people WHERE f_numb = " << nNumb;
int n = mysql_query(&m_sql, ss.str().c_str());
if (n)
{
CManager::PrintError("删除工号时发生错误");
return false;
}
return true;
}
void CWorker::Delete()
{//size_t n = mysql_affected_rows(&m_sql);
int nNumb;
cout << "请输入要删除的工号:";
cin >> nNumb;
auto row = FindNumb(nNumb);
if (!row)
{
puts("你输入的工号不存在!");
system("pause");
return;
}
PrintRow(row, _countof(m_ws));
printf("你确定要删除这条记录吗?[y/n]");
char c = 0;
cin >> c;//不出现类似于getchar 失效问题
if (c == 'Y' || c == 'y')
{
if(DeleteNumb(nNumb))
Print();
}
}
void CWorker::ModifyData(int nNumb)
{//size_t n = mysql_affected_rows(&m_sql);
string sName, sDate;
float fSala;
cout << "请输入姓名和工资、入职日期:";
cin >> sName >> fSala >> sDate;
char sSQL[256];
sprintf(sSQL, "UPDATE t_people SET f_name='%s',f_sala=%g,f_date='%s'"
" WHERE f_numb=%d", sName.c_str(),fSala, sDate.c_str(), nNumb);
if (mysql_query(&m_sql, sSQL))
CManager::PrintError("插入员工信息时出错");
else
Print();
}
void CWorker::Select(const char* sSQL)
{
int n = mysql_query(&m_sql, sSQL);
if (n)
{
CManager::PrintError("打印数据时发生错误:"+string(sSQL));
return;
}
auto res = mysql_store_result(&m_sql);
if (!res)
{
CManager::PrintError("打印数据时获取结果集错误:" + string(sSQL));
return;
}
system("cls");
const int ws[] = { 6,16,12,0 };
int i = -1;
cout << setiosflags(ios::left) << setw(ws[0]) << "工号" << setw(ws[1])
<< "姓名" << setw(ws[2]) << "工资" << "入职日期" << endl;
//puts("工号\t姓名\t工资\t入职日期");
MYSQL_ROW row;
int nCol = mysql_num_fields(res);
while (row = mysql_fetch_row(res))
PrintRow(row++, nCol);
cout << "总共有 " << mysql_num_rows(res) << " 条记录" << endl;
system("pause");
}
void CWorker::Modify()
{
int nNumb;
cout << "请输入要修改的工号:";
cin >> nNumb;
auto row = FindNumb(nNumb);
if (!row)
{
puts("你输入的工号不存在!");
system("pause");
return;
}
PrintRow(row, _countof(m_ws));
printf("你确定要修改这条记录吗?[y/n]");
char c = 0;
cin >> c;//不出现类似于getchar 失效问题
if (c == 'Y' || c == 'y')
ModifyData(nNumb);
}
void CWorker::Input()
{
int nNumb;
cout << "请输入工号:";
cin >> nNumb;
auto row = FindNumb(nNumb);
if (row)
{
cout << "你输入的工号已存在:";
PrintRow(row, _countof(m_ws));
system("pause");
return;
}
string sName,sDate;
float fSala;
cout << "请输入姓名和工资、入职日期:";
cin >> sName >> fSala>>sDate;
//stringstream ss;//sprintf
char sSQL[256];
sprintf(sSQL,"INSERT INTO t_people VALUES(%d,'%s',%g,'%s')", nNumb, sName.c_str(),
fSala, sDate.c_str());
if (mysql_query(&m_sql, sSQL))
CManager::PrintError("插入员工信息时出错");
else
Print();
//size_t n = mysql_affected_rows(&m_sql);
}
- 查找类:
CFind
具体通过哪些字段进行查找.
CFind.h
#pragma once
#include "define.h"
class CWorker;
class CFind
{
CWorker* m_pWorker;
void FindByNumb();
void FindByName();
void FindBySalary();
void FindByDate();
public:
int Menu();
CFind(CWorker* pWorker) :m_pWorker(pWorker)
{
}
};
CFind.cpp
#include "CFind.h"
#include "CWorker.h"
using namespace std;
void CFind::FindByNumb()
{
int nNumb;
printf("请输入要查找的工号:");
cin >> nNumb;
auto row = m_pWorker->FindNumb(nNumb);
if (row)
m_pWorker->PrintRow(row,4);
else
puts("你输入工号不存在!");
system("pause");
}
void CFind::FindByName()
{
string sName;//C++字符串
int nCount = 0;
cout << "请输入要查找的姓名(模糊查找):";
cin >> sName;
stringstream ss;
ss << "SELECT *FROM t_people WHERE f_name LIKE '%%" << sName << "%%'";
m_pWorker->Select(ss.str().c_str());
}
void CFind::FindBySalary()
{
float fMin, fMax;
printf("请输入要查的工资段(2个数字):");
cin >> fMin >> fMax;
char sSQL[256];
sprintf(sSQL, "SELECT * FROM t_people WHERE f_sala>=%g AND f_sala<=%g", fMin, fMax);
m_pWorker->Select(sSQL);
}
void CFind::FindByDate()
{
string sMin, sMax;
printf("请输入要查的日期段(yyyy-mm-dd yyyy-mm-dd):");
cin >> sMin >> sMax;
char sSQL[256];
sprintf(sSQL, "SELECT * FROM t_people WHERE f_date>='%s' AND f_date<='%s'",
sMin.c_str(), sMax.c_str());
m_pWorker->Select(sSQL);
}
int CFind::Menu()
{
system("cls");
puts("\n\n\t\t********************************");
puts("\t\t*\t1、按工号查找 *");
puts("\t\t*\t2、按姓名查找 *");
puts("\t\t*\t3、按工资段查找 *");
puts("\t\t*\t4、按日期段查找 *");
puts("\t\t*\t0、返回主查找 *");
puts("\t\t********************************");
printf("\t\t请选择:");
int n;
scanf_s("%d", &n);
switch (n)
{
case 1:
FindByNumb();
break;
case 2:
FindByName();
break;
case 3:
FindBySalary();
break;
case 4:
FindByDate();
break;
}
return n;
}
- 排序类:
CSort
CSort.h
#pragma once
class CWorker;
class CSort
{
CWorker* m_pWorker;
public:
int Menu();
CSort(CWorker* pWorker) :m_pWorker(pWorker)
{
}
};
CSort.cpp
#include "define.h"
#include "CSort.h"
#include "CWorker.h"
using namespace std;
int CSort::Menu()
{
system("cls");
//golang
puts("\n\n\t\t********************************");
puts("\t\t*\t1、按工号排序 *");
puts("\t\t*\t2、按姓名排序 *");
puts("\t\t*\t3、按工资排序 *");
puts("\t\t*\t4、按日期排序 *");
puts("\t\t*\t0、返回主菜单 *");
puts("\t\t********************************");
cout <<"\t\t请选择:";
int n;
cin >> n;
if (n > 0 && n < 5)
{
string ss[] = { "f_numb","f_name","f_sala","f_date"};
string sSQL = "SELECT * FROM t_people ORDER BY ";
sSQL += ss[n - 1];
m_pWorker->Select(sSQL.c_str());
}
return n;
}
总结
上面的程序主要是设计思路的过程,通过程序对MYSQL
常用的API
进行调用,来加深MYSQL API
的理解.