C++ 操作SQL Server
介紹
實現一下DBManager,DBManager是服務器程序的基礎模塊,但不是必選模塊。
程序接入
DBManager是程序的一種惰性模塊,所謂惰性模塊就是程序啓動初始化,等待某種事件調用,不用心跳更新。大部分服務器程序是熱衷模塊單例類,例如
魔獸世界服務器模塊
這程序主文件裏面聲明全局變量充當單例模式的角色。
初始化的時候調用初始化DB模塊,魔獸世界的編程喜歡定義一些宏代替單例代碼。
日月神教服務器數據庫模塊
本程序初始化
我這個就是一個測試
bool GameApp::Init()
{
DBConfig::InitDBConfig();
CoInitialize(NULL);
const char* cmd = "select * from [User]";
std::vector<UserModel*> Users = GameTable<UserModel>::Select(cmd);
for (size_t idx = 0; idx < Users.size(); idx++)
{
std::cout<<"id = "<<Users[idx]->Get_id() <<
", name = "<< Users[idx]->Get_name()<<
", sex = "<< Users[idx]->Get_sex() <<
", age = "<< Users[idx]->Get_age() << std::endl;
}
cmd = "select * from Tree";
std::vector<TreeModel*> Trees = GameTable<TreeModel>::Select(cmd);
for (size_t idx = 0; idx < Trees.size(); idx++)
{
std::cout<<"id = "<<Trees[idx]->Get_id()<<
", name = " <<Trees[idx]->Get_name() <<
", address = " <<Trees[idx]->Get_addr()<<
", height = "<<Trees[idx]->Get_height()<<
", grow = " <<Trees[idx]->Get_grow()<<std::endl;
}
CoUninitialize();
return true;
}
表操作業務模型。
業務模型反射
由於C++沒有反射機制,但是,如果使用數據庫不停的賦值業務模型的類成員變量是個比較繁瑣的代碼量,就需要反射一下。
#pragma once
#include<string>
#include "comutil.h"
#define DBTable(tableName, connectKey)\
public:\
static const char* GetTableName(){return tableName;}\
static const char* GetConnectKey(){return connectKey;}\
private:\
#define DBFieldInt(Field)\
public:\
void DBSet_##Field(_variant_t value){ Field = value.vt != VT_NULL ? value.operator int(): 0;}\
_variant_t DBGet_##Field(){ return _variant_t(Field);}\
void Set_##Field(int value){Field = value;}\
int Get_##Field(){return Field;}\
#define DBFieldBool(Field)\
public:\
void DBSet_##Field(_variant_t value){ Field =value.vt != VT_NULL ? value.operator bool() : false;}\
_variant_t DBGet_##Field(){ return _variant_t(Field);}\
void Set_##Field(bool value){Field = value;}\
bool Get_##Field(){return Field;}\
#define DBFieldFloat(Field)\
public:\
void DBSet_##Field(_variant_t value){ Field = value.vt != VT_NULL ? value.operator float() : 0.0f;}\
_variant_t DBGet_##Field(){ return _variant_t(Field);}\
void Set_##Field(float value){Field = value;}\
float Get_##Field(){return Field;}\
#define DBFieldString(Field)\
public:\
void DBSet_##Field(_variant_t value){ Field = value.vt != VT_NULL ? value.operator _bstr_t().operator const char *() : "";}\
_variant_t DBGet_##Field(){ return _variant_t(Field.c_str());}\
void Set_##Field(std::string value){Field = value;}\
std::string Get_##Field(){return Field;}\
#define InsertFieldMethod(TModel, Field, FieldName)\
{\
FieldMethod* temp = new FieldMethod(FieldName);\
temp->Setter = std::bind(&##TModel##::DBSet_##Field, this, std::placeholders::_1);\
temp->Getter = std::bind(&##TModel##::DBGet_##Field, this);\
m_FieldMethodTable.insert(std::make_pair(FieldName, temp));\
}\
#include <functional>
class FieldMethod
{
public:
FieldMethod(const char* field)
{
m_FieldName = field;
}
virtual ~FieldMethod(void)
{
}
public:
std::string FieldName() {return m_FieldName;}
private:
std::string m_FieldName;
public:
std::function<void (_variant_t )> Setter;
std::function<_variant_t ()> Getter;
};
在具體的業務模型裏面,描述需要反射的類型和成員。
#pragma once
#include "comutil.h"
#include<hash_map>
#include "FieldMethod.h"
#include "Tools.h"
class GameModel
{
public:
GameModel(void);
virtual ~GameModel(void);
protected:
typedef std::hash_map<const char*, FieldMethod*,std::hash_compare<const char*, const_char_less>> GameModelFieldTable;
GameModelFieldTable m_FieldMethodTable;
protected:
virtual void InitFieldMethodTable() = 0;
public:
void FieldSetValue(const char* name, _variant_t& value);
_variant_t FieldGetValue(const char* name);
};
定義一個純虛函數 InitFieldMethodTable,強迫子類描述反射函數。
#include "stdafx.h"
#include "GameModel.h"
GameModel::GameModel(void)
{
}
GameModel::~GameModel(void)
{
}
void GameModel::FieldSetValue(const char* name, _variant_t& value)
{
GameModelFieldTable::iterator itor = m_FieldMethodTable.find(name);
if (itor != m_FieldMethodTable.end())
{
itor->second->Setter(value);
}
}
_variant_t GameModel::FieldGetValue(const char* name)
{
GameModelFieldTable::iterator itor = m_FieldMethodTable.find(name);
if (itor != m_FieldMethodTable.end())
{
return itor->second->FieldName().c_str();
}
return _variant_t();
}
_variant_t 是一個關鍵,魔獸世界裏面也是用的同樣的思想,不過他自己封裝了一個結構。微軟提供了這個可變類型我就直接用了。
反射類型
#pragma once
#include "GameModel.h"
#include"FieldMethod.h"
#include <string>
class UserModel :
public GameModel
{
DBTable( "User", "GameDB")
public:
UserModel(void);
virtual ~UserModel(void);
private:
DBFieldInt(id)
int id;
DBFieldString(name)
std::string name;
DBFieldBool(sex)
bool sex;
DBFieldInt(age)
int age;
protected:
virtual void InitFieldMethodTable();
};
#include "stdafx.h"
#include "UserModel.h"
#include<functional>
UserModel::UserModel(void)
{
InitFieldMethodTable();
}
UserModel::~UserModel(void)
{
}
void UserModel::InitFieldMethodTable()
{
InsertFieldMethod(UserModel,id, "id")
InsertFieldMethod(UserModel, name, "name")
InsertFieldMethod(UserModel, sex, "sex")
InsertFieldMethod(UserModel, age, "age")
}
這樣通過宏描述反射類,讓類有反射代碼,實現成員和函數的反射。
數據庫表操作
只有一個查詢,代表操作數據庫表。
#pragma once
#include<vector>
#include<functional>
#include "FieldMethod.h"
#include "Tools.h"
//操作某種用戶模型,對所有表抽象 僅有查找功能
template<typename TGameModel>
class GameTable
{
public:
GameTable(void){}
virtual ~GameTable(void){}
public:
static std::vector<TGameModel*> Select(const char* cmd)
{
std::vector<TGameModel*> retVevc;
_ConnectionPtr pConnect(__uuidof(Connection));
_RecordsetPtr pRecord(__uuidof(Recordset));
pConnect->ConnectionString= DBConfig::GetDBConfig(TGameModel::GetConnectKey());
pConnect->Open("", "", "", adConnectUnspecified);
pRecord = pConnect->Execute((_bstr_t)(cmd), NULL, adCmdText);
if (!pRecord->BOF)
{
pRecord->MoveFirst();
std::vector<_bstr_t> col_name;
for (int idx = 0; idx < pRecord->Fields->GetCount(); idx++)
{
col_name.push_back(pRecord->Fields->GetItem(_variant_t((long)idx))->Name);
}
while (!pRecord->ADO_EOF)
{
TGameModel* temp = new TGameModel();
for(std::vector<_bstr_t>::iterator itor = col_name.begin();itor!=col_name.end();itor++)
{
temp->FieldSetValue(*itor, pRecord->GetCollect(*itor));
}
retVevc.push_back(temp);
pRecord->MoveNext();
}
}
pRecord->Close();
pConnect->Close();
return retVevc;
}
static bool Update(const char* cmd)
{
_ConnectionPtr pConnect(__uuidof(Connection));
_RecordsetPtr pRecord(__uuidof(Recordset));
_CommandPtr pCommand(__uuidof(Command));
pConnect->ConnectionString= DBConfig::GetDBConfig(TGameModel::GetConnectKey());
pConnect->Open("", "", "", adConnectUnspecified);
pRecord = pConnect->Execute((_bstr_t)(cmd), NULL, adCmdText);
if (!pRecord->BOF)
{
pRecord->MoveFirst();
}
pRecord->Close();
pConnect->Close();
return true;
}
};
其他雜類
數據庫連接字符串配置。hash_map字符串需要自己寫hash函數。
```cpp
#pragma once
#include <hash_map>
#include<functional>
struct const_char_less:
public std::binary_function<const char*, const char*, bool>
{
result_type operator()(const first_argument_type& left, const second_argument_type& right) const
{
return _stricmp(left, right) < 0 ? true : false;
}
};
typedef std::hash_map<const char*, const char*, std::hash_compare<const char*, const_char_less>> ConstCharHashMap;
class DBConfig
{
public:
static void InitDBConfig();
static const char* GetDBConfig(const char* szKey);
private:
static ConstCharHashMap ConnectTable;
};
運行結果
數據表
總結
我一開始想寫一個比較專業點的例子,至少代碼能用到產品裏面,後面發現需求不明確就會模糊概念,就只寫了一個查詢。
這個例子我感覺可貴之處是不用寫無腦的模型成員賦值,巧妙的地方是使用std::bind把類函數保存下來等待後來反射,C++反射機制類反射網上有不少博客,成員和函數反射解決方案相對少一些。
順帶我複習了一下 宏函數 哈希map 和 簡單的sql操作。
關鍵的地方是爲了驗證自己的想法擼了很多天代碼,看大神寫的代碼的確能提升水準。
最後
今天下載了一天UE4,仍然安裝失敗,來來回回好多次了,我還是繼續學習Unity吧~