[Visual C++] DB 연동 하기 – 3 (CDatabase, CRecordset)

저번 글까지 MariaDB를 설치하고, ODBC를 설치했습니다.
그리고 DATABASE test_db를 만들었고 TABLE test_table을 만들었구요.

아래 이미지를 보시면 테이블 구조를 보실 수 있습니다.

오늘은 DB 연동하기, 코딩쪽을 해보겠습니다.
MFC 프로젝트 하나 생성해 주시고, 아래와 같이 버튼 4개를 생성하시고 더블 클릭을 눌러 이벤트 처리 함수까지 만들어주세요.

그리고 대화상자 헤더에 아래 헤더파일을 include 하고 . CDatabase와 CRecordset 멤버변수를 선언합니다.

#include <afxdb.h>

// ....

CDatabase m_db;
CRecordset *m_pRs;

그리고 OnInitDialog에 아래와 같이 코딩합니다.

try
{
	BOOL bOpen = m_db.OpenEx( _T( "DSN=test_odbc;SERVER=127.0.0.1;PORT=3306;UID=root;PWD=root;DATABASE=test_db;" ), CDatabase::noOdbcDialog );
	if ( bOpen )
		m_pRs = new CRecordset( &m_db );
}
catch ( CException* e )
{
	e->ReportError();
}

OpenEx의 첫번째 인자에 문자열에 DSN, SERVER, PORT, UID, PWD, DATABASE을 보시면 전에 글에서 설정을 해줬던 것들이죠??

제대로 설정 하셨으면 OpenEx는 TRUE로 반환 합니다.

만약 제대로 설정 하셨는데도 아래와 같은 에러가 나타나면 솔루션 플랫폼을 ODBC와 맞춰주세요.

저같은 경우 64bit OS라서 MariaDb와 ODBC를 64bit로 설치했습니다. 그렇기 때문에 MFC 프로그램을 x64로 빌드하시고 실행하셔야 합니다.

위 작업을 모두 완료 하시면 이제 쿼리문을 만들 차례입니다.
Insert 버튼 이벤트 처리 함수에 아래와 같이 코딩합니다.

// Insert
void CDBTest03Dlg::OnBnClickedButton1()
{
	m_db.BeginTrans();

	try
	{
		m_db.ExecuteSQL( _T( "INSERT INTO TEST_TABLE(COL01, COL02) VALUES(1, 1)" ) );
		m_db.ExecuteSQL( _T( "INSERT INTO TEST_TABLE(COL01, COL02) VALUES(2, 2)" ) );
	}
	catch (CException* e)
	{
		e->ReportError();
	}

	m_db.CommitTrans();
}

mysql에서 select * from test_table;이라고 쿼리를 날리면 아래와 같이 표시됩니다.

정상적으로 입력됐네요. COL01이 PRIMARY라서 Insert 버튼을 두번 이상 누르면 안됩니다.

Select 버튼 이벤트 처리 함수에 아래와 같이 코딩합니다.

// Select
void CDBTest03Dlg::OnBnClickedButton2()
{
	try
	{
		CString sData(_T(""));
		BOOL bOpen = m_pRs->Open( CRecordset::snapshot, _T( "SELECT * FROM TEST_TABLE" ) );
		if ( bOpen )
		{
			int iRow = 1;
			BOOL bIsEOF = m_pRs->IsEOF();
			DWORD dwSize = m_pRs->GetRowsetSize();
			if ( !bIsEOF )
			{
				for ( m_pRs->MoveFirst() ; !m_pRs->IsEOF() ; m_pRs->MoveNext() )
				{
					int iFieldCnt = m_pRs->GetODBCFieldCount();
					for ( int iCol = 0 ; iCol < iFieldCnt ; iCol++ )
					{
						CString sItem;
						m_pRs->SetAbsolutePosition( iRow );
						m_pRs->GetFieldValue( iCol, sItem );
						if ( iCol == 0 )
							sData = sData + sItem;
						else
							sData = sData + _T( ", " ) + sItem;
					}
					sData += _T( "\n" );
					iRow++;
				}
			}
			AfxMessageBox( sData );
		}
	}
	catch ( CException* e )
	{
		e->ReportError();
	}

	m_pRs->Close();
}

실행해서 버튼을 클릭하면 데이터베이스에 입력과 똑같이 메시지를 출력합니다.

Update 버튼 이벤트 처리 함수에 아래와 같이 코딩합니다.

// Update
void CDBTest03Dlg::OnBnClickedButton3()
{
	m_db.BeginTrans();

	try
	{
		m_db.ExecuteSQL( _T( "UPDATE TEST_TABLE SET COL02=11 WHERE COL01=2" ) );
	}
	catch ( CException* e )
	{
		e->ReportError();
	}

	m_db.CommitTrans();
}

실행해서 버튼을 클릭한 뒤 Select 버튼을 클릭하면 아래와 같이 메시지를 출력합니다.

Delete 버튼 이벤트 처리 함수에 아래와 같이 코딩합니다.

// Delete
void CDBTest03Dlg::OnBnClickedButton4()
{
	m_db.BeginTrans();

	try
	{
		m_db.ExecuteSQL( _T( "DELETE FROM TEST_TABLE" ) );
	}
	catch ( CException* e )
	{
		e->ReportError();
	}

	m_db.CommitTrans();
}

실행해서 버튼을 클릭한 뒤 Select 버튼을 클릭하면 아래와 같이 메시지를 출력합니다.

대화상자가 종료 될 땐 아래와 같이 CRecordset과 CDatabase를 종료해주세요.

if ( m_pRs->IsOpen() )
	m_pRs->Close();
if ( m_db.IsOpen() )
	m_db.Close();

Insert, Update, Delete의 경우 코드가 생각보다 어렵지 않아서 금방 이해 될 겁니다.
하지만 Select의 경우 쿼리문을 날릴 뿐만 아니라 Select의 결과를 담아야할 객체가 필요한데 CRecordset이 그 역할을 해줍니다.

CDatabase는 데이터베이스와의 연결(커넥션)이라고 하는 객체고,
CRecordset은 쿼리의 결과(레코드)를 담는 객체라고 생각하시면 됩니다.

사실 저도 DB를 위주로 다뤄보지 않아 하면서 가물가물 했네요.
확실히 DB를 전문적으로 다루는 사람이 괜히 있는게 아닌것 같습니다.

그래도 이정도면 기본적인 쿼리문은 사용하실수 있을겁니다.