下面的代码实现连接数据库的功能,但是用的是全局函数实现。
函数也就罢了,里面的变量又用到了全局变量来访问数据库,由于多线程调用,所以还要支持多线程。
多线程访问全局变量就要支持并发,加临界区(或者说Guard)。
访问数据库又有可能出错,出错又要退出,退出又要在流程上处理错误处理,临界区控制更麻烦。
只要哪里写的不完备,你懂的,多线程临界区出了问题,多个线程的调用全局变量,这些线程就会像一把把锋利的匕首一样从你的四面向你刺来。你可能死都不知道是被谁弄死的。
让我们来看看这个超级烂的烂代码吧:
如何改进呢?
1 功能封装到class里
2 new的对象用类的普通成员变量表示(不是指针成员变量)
3 访问数据库的时候在线程内创建栈对象(这样不同的线程就会使用不同的栈对象,互不关联,免去了临界区那些个同步垃圾代码)
改进后的代码基本上是下面这个样子:
#include <string>
#include <Windows.h>
class QueryResult {};//第三方库的代码:仅仅作为示例
class ADO //第三方库的代码:仅仅用来说明:真正操作数据库的客户端类(或者函数族)
{
public:
bool Connect(const std::string& connectInfor) { return true; }//仅仅作为示例:连接数据库
bool Query(const std::string& querySql, QueryResult& result) { return true; }//仅仅作为示例:增删改查只用查询做例子
bool Disconnect(void) { return true; }//断开连接数据库
};
class DataBaseHelper //你的代码:你使用这个类来访问数据库(主要封装连接和断开连接)
{
public:
bool Connect(const std::string& connectInfor) { return m_databaseHandle.Connect(connectInfor); }//仅仅作为示例
bool Query(const std::string& querySql, QueryResult& result) { return m_databaseHandle.Query(querySql, result); }//仅仅作为示例
~DataBaseHelper();//析构函数负责断开连接
private:
ADO m_databaseHandle;//真正操作数据库的类(或者函数族)
};
DataBaseHelper::~DataBaseHelper()
{
m_databaseHandle.Disconnect();//程序员不需要记住要断开连接,这里总是自动调用(无需使用new动态对象)
}
void FunctionUseDatabaseExample(void)//这个函数代表所有访问数据库的模块(或者是线程函数)
{
//有了以上的代码:我们就可以自由的使用临时变量(栈对象)访问数据库了
DataBaseHelper dbHelper;//注意这里是临时变量,所以多线程是安全的(每个线程函数内使用各自的临时变量,不存在临界区)
std::string databaseInfor("root/123456@my_odbc_name");
if (!dbHelper.Connect(databaseInfor))//每次访问数据库之前都先连接,断开连接由析构函数调用
{
//错误处理程序,通常是退出当前函数,不往后执行;打印日志;或者同时弹出弹框提示(客户端)
}
//正常的操作数据库的增删改查在这里进行
std::string querySql("balabala");
QueryResult result;
if (!dbHelper.Query(querySql, result))
{
//错误处理程序,通常是退出当前函数,不往后执行;打印日志;或者同时弹出弹框提示(客户端)
}
//继续走后续流程
//to do your other work
while (true)
{
dbHelper.Query(querySql, result);//这里表示有频繁访问数据库的需要
int sometime = 1000 * 2;//2秒访问一次;如果更长时间(比如50秒,超过了连接超时时间),可以在这里再次使用循环内的临时变量(替代本函数一开始创建的dbHelper)
Sleep(sometime);//假设这里是一个线程函数的话
}
}//这里释放dbHelper,析构函数会断开与数据库的本次连接
int main()
{
FunctionUseDatabaseExample();
return 0;
}
以上DataBaseHelper类对象随用随创建,不用手动断开连接;可以被反复使用,安全可靠,省心。
其实真正的封装就这几行代码: