用c/c++操作 SQL Server
阿新 • • 發佈:2021-02-11
用c/c++操作 SQL Server
DatabaseHelper工具類,直接用或用作基類,各個方法提供了簡要的註釋。已測試,支援呼叫儲存過程。
DatabaseHelper.h
#pragma once
#include"../Common.h"
#include<Windows.h>
#include<sql.h>
#include<sqlext.h>
#include<tchar.h>
#include<vector>
#include<type_traits>
#include<string>
#include<map>
using std::string;
using std::wstring;
#ifdef UNICODE
using String = wstring;
#else
using String = string;
#endif
//通用資料庫工具類
class DatabaseHelper
{
public:
typedef struct {
bool bOmit; //該引數是否跳過
int iSqlCType; //對應c語言的型別
int iSqlType; //對應sql的型別
int iColSize; //寬度最大值
int iDigits; //最大精度
bool bOutput; //是否為輸出函式
void* pData; //資料地址
SQLLEN* piLengthBuffer;//接收長度的記憶體
int iLength;//資料長度
} ProcParam; //執行儲存過程的引數
public:
using ExecProcParam = std::vector<ProcParam>;
using SqlAttrMap = std::map<SQLINTEGER, SQLPOINTER>;
DatabaseHelper() : m_hDBC(nullptr),m_hEnv(nullptr) { m_cchMaxStrLength = 1024; }
virtual ~DatabaseHelper();
//功能函式
//連線到資料庫
BOOL Connect(PCTCH pszServer, PCTCH pszDatabase, PCTCH uid = nullptr, PCTCH pszPassword = nullptr);
BOOL Disconnect();
//讀取某一列(SELECT),表名,列名,行數,接收結果的緩衝區
//函式將根據緩衝區型別選擇資料型別
//SELECT $pszRow FROM $pszTable WHERE $pszConditions ...
template<typename T>
size_t Select(PCTCH pszTable, PCTCH pszRow, T* pBuffer,size_t cbBufferSize, PCTCH pszConditions);
//讀取一列字串,ANSI格式
std::vector<string> SelectString(PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions = nullptr);
//讀取一列字串,UNICODE格式
std::vector<wstring> SelectWString(PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions = nullptr);
//UPDATE $pszTable SET $pszRow1 = $pszValue1 WHERE $pszRow2 = $pszValue2
BOOL Update(PTCHAR pszTable, PTCHAR pszRow1, PTCHAR pszValue1, PTCHAR pszRow2, PTCHAR pszValue2);
//DELETE FROM $pszTable WHERE $pszRow = $pszValue
BOOL Delete(PTCHAR pszTable, PTCHAR pszRow, PTCHAR pszValue);
//INSERT INTO $pszTable VALUES $values
BOOL Insert(PTCHAR pszTable, PTCHAR * ppszValues, size_t uValueNums);
//執行儲存過程
BOOL ExecProc(const String &strProc,const std::vector<ProcParam>& vParams);
auto GetServerName() const noexcept{
return m_strServer;
}
auto GetDatabaseName() const noexcept {
return m_strDatabase;
}
private:
//執行任意一條命令
BOOL Command(PCTCH pszCommand, const SqlAttrMap& mapAttr = SqlAttrMap(),
const SQLINTEGER* pLengths = nullptr, SQLHSTMT* phStmt = nullptr);
//執行Select命令並完成相應型別的繫結
SQLHSTMT SelectAndBind(PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions,
void* pBuffer, size_t cbBufferSize,
SQLLEN* pcbBytes, SQLSMALLINT sqlType);
//通過資料型別得到SQL型別的編碼
template <typename T>
static constexpr SQLSMALLINT GetTargetType() noexcept;
String m_strServer;
String m_strDatabase;
SQLHDBC m_hDBC;
SQLHENV m_hEnv;
size_t m_cchMaxStrLength;//讀取字串的最大長度
};
template<typename T>
inline size_t DatabaseHelper::Select(
PCTCH pszTable, PCTCH pszRow,
T* pBuffer, size_t cbBufferSize,
PCTCH pszConditions)
{
//查詢資料表
//查詢的結果儲存到這些變數
String strCommand;
SqlAttrMap map;
SQLHSTMT hStmt = nullptr; //語句控制代碼
SQLRETURN ret = SQL_SUCCESS;
SQLLEN cbBytes = 0;
size_t nums = 0; //讀取的行數
strCommand = strCommand + (_T("SELECT ")) + pszRow + _T(" FROM ") + pszTable;
if (pszConditions && _tcslen(pszConditions))
strCommand = strCommand + _T(" WHERE ") + pszConditions;
map[SQL_ATTR_ROW_BIND_TYPE] = (SQLPOINTER)SQL_BIND_BY_COLUMN;
SQLINTEGER cbValueMax = SQL_IS_INTEGER;
ret = Command(strCommand.c_str(), map, &cbValueMax, &hStmt);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
auto iTargetType = GetTargetType<T>();
T* temp = nullptr;
if (iTargetType == SQL_C_CHAR || iTargetType == SQL_C_WCHAR) {
int p = 0;
temp = new T[m_cchMaxStrLength]();
ret = SQLBindCol(hStmt, 1, iTargetType, temp, m_cchMaxStrLength, &cbBytes);
for (int p = 0; (ret = SQLFetch(hStmt)) != SQL_NO_DATA; p += cbBytes / sizeof(T) + 1)
{
//cbBytes 為SQLFetch讀取的位元組數,如果為字串,不包括\0
memcpy_s(pBuffer + p, cbBufferSize - p, temp, cbBytes + sizeof(T));
}
delete[] temp;
}
else {
temp = new T;
ret = SQLBindCol(hStmt, 1, iTargetType, temp, m_cchMaxStrLength, &cbBytes);
for (int i = 0; (ret = SQLFetch(hStmt)) != SQL_NO_DATA; i++) {
pBuffer[i] = *temp;
}
delete temp;
}
}
// 關閉控制代碼
if(hStmt)
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return nums;
}
template<typename T>
inline constexpr SQLSMALLINT DatabaseHelper::GetTargetType() noexcept
{
using Type = std::remove_cv_t<T>;
SAMETHENRETURN(Type, char, SQL_C_CHAR);
SAMETHENRETURN(Type, unsigned char, SQL_C_CHAR);
SAMETHENRETURN(Type, wchar_t, SQL_C_WCHAR);
SAMETHENRETURN(Type, double, SQL_C_DOUBLE);
SAMETHENRETURN(Type, long, SQL_C_LONG);
SAMETHENRETURN(Type, int, SQL_C_LONG);
SAMETHENRETURN(Type, float, SQL_C_FLOAT);
SAMETHENRETURN(Type, short int, SQL_C_SHORT);
SAMETHENRETURN(Type, unsigned long int, SQL_C_ULONG);
SAMETHENRETURN(Type, DATE_STRUCT, SQL_C_TYPE_DATE);
SAMETHENRETURN(Type, TIME_STRUCT, SQL_C_TYPE_TIME);
}
DatabaseHelper.cpp
#include "DatabaseHelper.h"
#include<tchar.h>
#include<sqlext.h>
#include<algorithm>
#include<stdexcept>
DatabaseHelper::~DatabaseHelper()
{
Disconnect();
}
BOOL DatabaseHelper::Connect(PCTCH pszServer, PCTCH pszDatabase, PCTCH pszUID, PCTCH pszPassword)
{
SQLHENV env = nullptr; // 環境控制代碼
SQLHDBC dbc = nullptr; // 資料庫連線控制代碼
SQLRETURN ret = SQL_SUCCESS; // 返回值,用於檢查是否正確
//分配環境控制代碼
ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
//設定環境控制代碼屬性
ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
//根據環境控制代碼分配相應的連線控制代碼
ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
String strConnect = _T("Driver={SQL Server};");
strConnect = strConnect + _T("SERVER=") + pszServer + _T(";") + _T("DATABASE=") + pszDatabase + _T(";");
if (pszUID) {
strConnect = strConnect + _T("UID=") + pszUID + _T(";");
}
if (pszPassword) {
strConnect = strConnect + _T("PWD=") + pszPassword + _T(";");
}
//已確保強制轉換是安全的...
ret = SQLDriverConnect(dbc, nullptr, (SQLTCHAR*)strConnect.data(), SQL_NTS, nullptr, 0, nullptr, SQL_DRIVER_COMPLETE);
if(ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
m_hDBC = dbc;
m_hEnv = env;
m_strDatabase = pszDatabase;
m_strServer = pszServer;
return TRUE;
}
return FALSE;
}
BOOL DatabaseHelper::Disconnect()
{
// 關閉連線,釋放環境
if (m_hDBC) {
SQLDisconnect(m_hDBC);
SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC);
m_hDBC = nullptr;
}
if (m_hEnv) {
SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv);
m_hEnv = nullptr;
}
return TRUE;
}
BOOL DatabaseHelper::Command(
PCTCH pszCommand, const SqlAttrMap& mapAttr,
const SQLINTEGER* pLengths, SQLHSTMT* phStmt)
{
SQLHSTMT hStmt = nullptr; //語句控制代碼
SQLRETURN ret = SQL_SUCCESS;
// 申請控制代碼
ret = SQLAllocHandle(SQL_HANDLE_STMT, m_hDBC, &hStmt);
//設定屬性
int i = 0;
for (const auto& pair : mapAttr) {
SQLSetStmtAttr(hStmt, pair.first, pair.second, pLengths[i++]);
}
//確保強制轉換的正確性
ret = SQLExecDirect(hStmt, (SQLTCHAR*)pszCommand, SQL_NTS);
if (hStmt && phStmt == nullptr) {
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
else {
*phStmt = hStmt;
}
return ret;
}
SQLHSTMT DatabaseHelper::SelectAndBind(
PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions,
void* pBuffer, size_t cbBufferSize,
SQLLEN* pcbBytes, SQLSMALLINT sqlType)
{
String strCommand;
SqlAttrMap map;
SQLHSTMT hStmt = nullptr; //語句控制代碼
SQLRETURN ret = SQL_SUCCESS;
strCommand = strCommand + (_T("SELECT ")) + pszRow + _T(" FROM ") + pszTable;
if (pszConditions && _tcslen(pszConditions))
strCommand = strCommand + _T(" WHERE ") + pszConditions;
map[SQL_ATTR_ROW_BIND_TYPE] = (SQLPOINTER)SQL_BIND_BY_COLUMN;
SQLINTEGER cbValueMax = SQL_IS_INTEGER;
ret = Command(strCommand.c_str(), map, &cbValueMax, &hStmt);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
ret = SQLBindCol(hStmt, 1, sqlType, pBuffer, cbBufferSize, pcbBytes);
}
return hStmt;
}
std::vector<string> DatabaseHelper::SelectString(PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions)
{
std::vector<string> result;
auto wstrs = SelectWString(pszTable, pszRow, pszConditions);
result.reserve(wstrs.size());
char* pszBuffer = new char[m_cchMaxStrLength];
for (const auto& wstr : wstrs) {
string str;
size_t len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), nullptr, 0, nullptr, nullptr);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), pszBuffer, len, nullptr, nullptr);
pszBuffer[len] = 0;
str = pszBuffer; //將轉換過的字串拷貝給str
result.push_back(std::move(str)); //移動構造
}
delete[] pszBuffer;
return result;
}
std::vector<wstring> DatabaseHelper::SelectWString(PCTCH pszTable, PCTCH pszRow, PCTCH pszConditions)
{
//查詢資料表
//查詢的結果儲存到這些變數
String strCommand;
SQLHSTMT hStmt = nullptr; //語句控制代碼
SQLRETURN ret = SQL_SUCCESS;
std::vector<wstring> result; //返回結果
WCHAR* pBuffer = new WCHAR[m_cchMaxStrLength]();
//執行命令並繫結
hStmt = SelectAndBind(pszTable, pszRow, pszConditions,
pBuffer, m_cchMaxStrLength, nullptr, SQL_C_WCHAR);
//存取
if (hStmt != nullptr && hStmt != INVALID_HANDLE_VALUE) {
while ((ret = SQLFetch(hStmt)) != SQL_NO_DATA && ret != SQL_ERROR) {
result.emplace_back(pBuffer);
}
delete[] pBuffer;
}
// 關閉控制代碼
if (hStmt)
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
if (ret == SQL_ERROR) {
throw std::runtime_error("執行錯誤!");
}
return result;
}
//UPDATE $pszTable SET $pszRow1 = $pszValue1 WHERE $pszRow2 = $pszValue2
BOOL DatabaseHelper::Update(
PTCHAR pszTable,
PTCHAR pszRow1, PTCHAR pszValue1,
PTCHAR pszRow2,PTCHAR pszValue2)
{
String strCommand = _T("UPDATE ");
strCommand = strCommand + pszTable + _T(" SET ") + pszRow1 + _T('=') + pszValue1 +
_T(" WHERE ") + pszRow2 + _T('=') + pszValue2 + _T(';');
return Command(strCommand.c_str());
}
//DELETE FROM $pszTable WHERE $pszRow = $pszValue
BOOL DatabaseHelper::Delete(PTCHAR pszTable, PTCHAR pszRow, PTCHAR pszValue)
{
String strCommand = _T("DELETE FROM ");
strCommand = strCommand + pszTable + _T(" WHERE ") + pszRow + _T('=') + pszValue + _T(';');
return Command(strCommand.c_str());
}
//INSERT INTO $pszTable VALUES $values
BOOL DatabaseHelper::Insert(PTCHAR pszTable, PTCHAR * ppszValues, size_t uValueNums)
{
String strCommand = _T("INSERT INTO ");
if (uValueNums <= 0)
return FALSE;
strCommand = strCommand + pszTable + _T(" VALUES (");
for (size_t i = 0; i < uValueNums-1; i++) {
strCommand = strCommand + ppszValues[i] + _T(',');
}
strCommand = strCommand + ppszValues[uValueNums - 1] + _T(')') + _T(';');
return Command(strCommand.c_str());
}
BOOL DatabaseHelper::ExecProc(const String& strProc, const std::vector<ProcParam>& vParams)
{
SQLHSTMT hStmt = nullptr; //語句控制代碼
SQLRETURN ret = SQL_SUCCESS;
// 申請控制代碼
ret = SQLAllocHandle(SQL_HANDLE_STMT, m_hDBC, &hStmt);
if (!hStmt)
return FALSE;
String strExec;
for(int i=0;i<vParams.size();i++) {
const auto& p = vParams[i];
if (p.bOmit)
continue;
SQLBindParameter(hStmt,
i+1, //索引從1開始
p.bOutput ? SQL_PARAM_INPUT_OUTPUT : SQL_PARAM_INPUT,
p.iSqlCType,
p.iSqlType,
p.iColSize,
p.iDigits,
p.pData,
p.iLength,
p.piLengthBuffer);
}
strExec = _T("{ CALL ") + strProc;
//如果有引數就加上括號
if (!vParams.empty()) {
strExec += _T('(');
for (int i = 0; i < vParams.size(); i++) {
if (!vParams[i].bOmit) {
strExec += _T('?');
}
strExec += _T(',');
}
strExec.back() = _T(')');
}
strExec += _T('}');
SQLExecDirect(hStmt, const_cast<SQLTCHAR*>(strExec.c_str()), SQL_NTS);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return TRUE;
}
common.h
#pragma once
#include<type_traits>
//T1 和 T2 型別相同則返回ret
#define SAMETHENRETURN(T1,T2,ret) \
if(std::is_same_v<T1,T2>) \
return (ret);
//a,b相等則返回ret
#define EQUALTHENRETURN(a,b,ret) \
if((a) == (b)) \
return (ret);