(C++ : IOCP-59) DBConnection

Posted by : at

Category : Cpp   iocp


참고로 본 강의에서 모든 DB에 대한 내용을 다 다루지는 못한다.
대충 이렇게 한다는 흐름만 이해하고 필요한 부분이 있으면 찾아서 적용해야한다.


DB 연동

  • DBConnectionPool
  • DBConnection

두 개의 클래스를 이용해서 DB연결을 담당

#pragma once
#include "DBConnection.h"

class DBConnectionPool
{
public:
	DBConnectionPool();
	~DBConnectionPool();

	bool					Connect(int32 connectionCount, const WCHAR* connectionString);
	void					Clear();

	DBConnection*			Pop();
	void					Push(DBConnection* connection);

private:
	USE_LOCK;
	SQLHENV					_environment = SQL_NULL_HANDLE;
	Vector<DBConnection*>	_connections;                       
    // 사용변수를 보면 대충느낌이 오겠지만
    // DBConnection를 여러개 만들어서 관리 하며
    // DB의 환경정보 SQLHENV를 적용할 것이란것을 알 수 있다.
};
// DB와 연결을 담당
bool DBConnectionPool::Connect(
                        int32 connectionCount,          // 몇개를 연결할 것인가
                        const WCHAR* connectionString)  // 연결의 조건은
{
	WRITE_LOCK;

    // 핸들을 할당해 주세요
	if (::SQLAllocHandle(SQL_HANDLE_ENV, 	// 핸들 할당
						SQL_NULL_HANDLE, 
						&_environment) 		// 여기에 할당해 주세요
						!= SQL_SUCCESS)
		return false;

    // 핸들의 Attr를 선언해주세요
    // 여기서는 버전을 할당
	if (::SQLSetEnvAttr(_environment, 									// 환경은
						SQL_ATTR_ODBC_VERSION, 							// 버전은
						reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3), 	// ODBC 3입니다
						0) != SQL_SUCCESS)
		return false;

    // 몇개의 연결을 맺을 것인지
	for (int32 i = 0; i < connectionCount; i++)
	{
		DBConnection* connection = xnew<DBConnection>();
        // _environment, connectionString을 Connection 하면서 보냄을 기억하자
		if (connection->Connect(_environment, connectionString) == false)
			return false;

		_connections.push_back(connection);
	}

	return true;
}

나머지 함수는 간단하기에 생략

#pragma once
#include <sql.h>
#include <sqlext.h>

class DBConnection
{
public:
	bool			Connect(SQLHENV henv, const WCHAR* connectionString);
	void			Clear();

	bool			Execute(const WCHAR* query);    // 쿼리 명령실행
	bool			Fetch();                        // 결과를 받는다
	int32			GetRowCount();                  // 행 개수
	void			Unbind();

public:
	bool			BindParam(SQLUSMALLINT paramIndex, 
							SQLSMALLINT cType, 
							SQLSMALLINT sqlType, 
							SQLULEN len, 
							SQLPOINTER ptr, 
							SQLLEN* index);

	bool			BindCol(SQLUSMALLINT columnIndex, 
							SQLSMALLINT cType, 
							SQLULEN len, 
							SQLPOINTER value, 
							SQLLEN* index);

	void			HandleError(SQLRETURN ret);

private:
	SQLHDBC			_connection = SQL_NULL_HANDLE;      // DB핸들정보
	SQLHSTMT		_statement = SQL_NULL_HANDLE;       // 상태정보
};
bool DBConnection::Connect(SQLHENV henv, const WCHAR* connectionString)
{
    // DBC 핸들할당
	if (::SQLAllocHandle(SQL_HANDLE_DBC, henv, &_connection) != SQL_SUCCESS)
		return false;

	WCHAR stringBuffer[MAX_PATH] = { 0 };
	::wcscpy_s(stringBuffer, connectionString);

    // 결과를 담는다
	WCHAR resultString[MAX_PATH] = { 0 };
	SQLSMALLINT resultStringLen = 0;

    // SQLDriverConnectW를 이용해 DB를 Connect한다고 이해하자
	SQLRETURN ret = ::SQLDriverConnectW(
		_connection,
		NULL,
		reinterpret_cast<SQLWCHAR*>(stringBuffer),
		_countof(stringBuffer),
		OUT reinterpret_cast<SQLWCHAR*>(resultString),
		_countof(resultString),
		OUT & resultStringLen,
		SQL_DRIVER_NOPROMPT
	);

	if (::SQLAllocHandle(SQL_HANDLE_STMT, _connection, &_statement) != SQL_SUCCESS)
		return false;

	return (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO);
}
// 쿼리 실행요청
bool DBConnection::Execute(const WCHAR* query)
{
	SQLRETURN ret = ::SQLExecDirectW(_statement, (SQLWCHAR*)query, SQL_NTSL);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
		return true;

	HandleError(ret);
	return false;
}
bool DBConnection::BindParam(SQLUSMALLINT paramIndex, SQLSMALLINT cType, SQLSMALLINT sqlType, SQLULEN len, SQLPOINTER ptr, SQLLEN* index)
{
    // 몇 번째 인자를 뭘로 선언할 것인지 등을 선언
    // 이후 실 사용예제로 보면 더 쉽다
	SQLRETURN ret = ::SQLBindParameter(_statement, 
									paramIndex, 
									SQL_PARAM_INPUT, 
									cType, 
									sqlType, 
									len, 0, ptr, 0, index);

	if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
	{
		HandleError(ret);
		return false;
	}

	return true;
}

실제 사용은 이렇게 한다.

int main()
{
	// DB Connect(이 부분은 어떻게 찾는지 아래서 다시 설명)
	ASSERT_CRASH(GDBConnectionPool->Connect(1, L"Driver={SQL Server Native Client 11.0};Server=(localdb)\\MSSQLLocalDB;Database=ServerDb;Trusted_Connection=Yes;"));

	// Create Table
	{
        // 테이블이 있으면 삭제 후 다시 생성
		auto query = L"									\
			DROP TABLE IF EXISTS [dbo].[Gold];			\
			CREATE TABLE [dbo].[Gold]					\
			(											\
				[id] INT NOT NULL PRIMARY KEY IDENTITY, \
				[gold] INT NULL							\
			);";

        // db connection을 하나 빼온다.
		DBConnection* dbConn = GDBConnectionPool->Pop();
		ASSERT_CRASH(dbConn->Execute(query));
		GDBConnectionPool->Push(dbConn);
	}

	// Add Data
	for (int32 i = 0; i < 3; i++)
	{
		DBConnection* dbConn = GDBConnectionPool->Pop();
		// 기존에 바인딩 된 정보 날림
		dbConn->Unbind();

		// 넘길 인자 바인딩
		int32 gold = 100;
		SQLLEN len = 0;

		// 넘길 인자 바인딩
		ASSERT_CRASH(dbConn->BindParam(1, SQL_C_LONG, SQL_INTEGER, sizeof(gold), &gold, &len));

		// SQL 실행
		ASSERT_CRASH(dbConn->Execute(L"INSERT INTO [dbo].[Gold]([gold]) VALUES(?)"));

		GDBConnectionPool->Push(dbConn);
	}

	// Read
	{
		DBConnection* dbConn = GDBConnectionPool->Pop();
		// 기존에 바인딩 된 정보 날림
		dbConn->Unbind();

		int32 gold = 100;
		SQLLEN len = 0;
		// 넘길 인자 바인딩
		ASSERT_CRASH(dbConn->BindParam(1, SQL_C_LONG, SQL_INTEGER, sizeof(gold), &gold, &len));

		int32 outId = 0;
		SQLLEN outIdLen = 0;
		ASSERT_CRASH(dbConn->BindCol(1, SQL_C_LONG, sizeof(outId), &outId, &outIdLen));

		int32 outGold = 0;
		SQLLEN outGoldLen = 0;
		ASSERT_CRASH(dbConn->BindCol(2, SQL_C_LONG, sizeof(outGold), &outGold, &outGoldLen));

		// SQL 실행
		ASSERT_CRASH(dbConn->Execute(L"SELECT id, gold FROM [dbo].[Gold] WHERE gold = (?)"));

		while (dbConn->Fetch())
		{
			cout << "Id: " << outId << " Gold : " << outGold << endl;
		}

		GDBConnectionPool->Push(dbConn);
	}


DB 세팅하기

목차 -> 보기 -> SQL Server 개체 탐색기

(만약에 뜨지않으면 Visual Studio Installer에서 DB관련 패키지를 설치해야함)

데이터베이스 우클릭 후 새 DB생성

생성한 Db에서 연결 문자열을 기록해 둔다

기록된 연결문자열을 아래에 사용한다.

int main()
{

	// DB Connect
	ASSERT_CRASH(GDBConnectionPool->Connect(1, L"Driver={SQL Server Native Client 11.0};Server=(localdb)\\ProjectsV13;Database=ServerDb;Trusted_Connection=Yes;"));

	// Create Table
	{

		// ...

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links