1. 程式人生 > 其它 >用c/c++操作 SQL Server

用c/c++操作 SQL Server

技術標籤:資料庫sqlc++sqlserver

用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);