티스토리 뷰
ODBC에 액세스 DB 추가 방법
1. 제어파에서 관리도구를 실행하면 아래 창이 뜨고 이떄 ODBC 데이터 원본(64비트) 를 선택한다.
(32비트로 선택하면 추가 안됨)
2. 사용자 DSN 탭이 선택된 상태에서 추가 버튼을 누른다.
3. Microsoft Access Driver 선택
4. 데이터 원본 이름에 student 라 입력하고 선택 버튼을 누른다.
5. 액세스 DB파일을 선택해주고 확인 버튼을 누른다.
6. 최종적으로 사용자 DSN에 student 가 추가된것을 확인할 수 있다.
이대로 실행하면 아래 창을 보이면서 에러 난다.
할수없이 ODBC 대신 DB파일을 직접 지정해주는 코드로 수정했다. (accdb 파일은 여기서도 동일 증상 발생함)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 |
#include <afxdb.h>
#include <conio.h>
int main()
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(nullptr);
if (hModule != nullptr)
{
// MFC를 초기화합니다. 초기화하지 못한 경우 오류를 인쇄합니다.
if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
{
// TODO: 오류 코드를 필요에 따라 수정합니다.
wprintf(L"심각한 오류: MFC를 초기화하지 못했습니다.\n");
nRetCode = 1;
}
else
{
// TODO: 응용 프로그램의 동작은 여기에서 코딩합니다.
CDatabase db;
/******
BOOL b = db.OpenEx("DSN=student", 0);
// 원래 ODBC에서 열어야 하나 윈도우10에서 실행하면 OS에서 실행할 수 없다고 에러 창 뜬다.
*******/
CString strConnection("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=Student.mdb;UID=;PWD=");
if (db.OpenEx(strConnection/*, CDatabase::noOdbcDialog*/) == FALSE)
{
cout << (LPCTSTR)"데이터베이스 접속실패!" << endl;
_getch();
return nRetCode;
}
CRecordset rs(&db);
rs.Open(CRecordset::dynaset, "SELECT * FROM Table1");
// 레코드 출력
CString str;
while (!rs.IsEOF())
{
rs.GetFieldValue(short(0), str);
cout << (LPCTSTR)str << " ";
rs.GetFieldValue(short(1), str);
cout << (LPCTSTR)str << " ";
rs.GetFieldValue(short(2), str);
cout << (LPCTSTR)str << " ";
rs.GetFieldValue(short(3), str);
cout << (LPCTSTR)str << endl;
rs.MoveNext();
}
rs.Close();
db.Close();
_getch(); // 도스창 확인용
}
}
else
{
// TODO: 오류 코드를 필요에 따라 수정합니다.
wprintf(L"심각한 오류: GetModuleHandle 실패\n");
nRetCode = 1;
}
return nRetCode;
} |
cs |
1. DB 객체 생성
BOOL CDatabase::OPenEx(LPCTSTR lpszConnectString, DWORD dwOptions = 0);
lpszConnectString
사용할 데이터 원본 이름과 ID, 암호 등을 문자열로 넘겨준다. ID와 암호가 없는 경우 위 코드 처럼 생략해도 된다. 데이터 원본 이름과 ID, 암호를 모두 사용한다면 아래 같이 한다.
"DSN=Student;UID=SA;PWD=abc123"
dwOptions
비트 매스크로 옵션을 지정한다. 기본값인 0을 사용하면 읽기와 쓰기 모드로 DB룰 접근하며, lpszConnectString 정보가 충분치 않을 경우 자동으로 대화상자가 열리고 이때 DB파일을 지정해주면 된다.
2. 레코드셋 객체 생성
CRecordset::CRecordset(CDatabase* pDatabase = NULL);
pDatabase
DB 객체의 주소를 나타낸다. 클래스 위저드를 이용하여 코드를 생성한 경우에는 pDatabase 에 기본값인 NULL을 사용하는 경우가 많으며, 이 경우 CRecordset 클래스에서 내부적으로 DB 객체를 생성한다.
BOOL CRecordset::OpenEx(UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL = NULL, DWORD dwOptions = none);
nOpenType
nOpenType 종류 |
의미 |
CRecordset::dynaset |
양방향 스크롤을 지원하며, 다른 사용자가 변경한 대부분의 내용(레코드 삽입은 제외)이 레코드셋에 반영된다. |
CRecordset::snapshot |
양방향 스크롤을 지원하며, 다른 사용자가 변경한 내용이 레코드셋에 반영되지 않는다. |
CRecordset::dynamic |
양방향 스크롤을 지원하며, 다른 사용자가변경한 모든 내용이 레코드셋에 반영된다. 시스템 리소스를 가장 많이 사용하며, ODBC 드라이버에 따라 지원하지 않는 경우도 있으므로 주의해야 한다. |
CRecordset::forwardOnly |
전방향(Forward) 스크롤만 지원하며, 레코드를 읽는 것만 가능하다. 시스템 리소스를 가장 적게 사용한다. |
lpszSQL
SQL SELECT 문을 여기에 사용하면 조건에 맞는 레코드셋을 얻을 수 있다.
dwOptions
읽기 전용(CRecordset::readOnly), 추가전용(CRecordset::appendOnly) 등 다양한 옵션이 있으니, MSDN을 참조하기 바란다.
3. 레코드 출력
CRecordset::Open()을 이용하여 레코드셋을 얻으면, 레코드셋 객체는 항상 현재 레코드(Current Record)를 가지고 있게 된다. CRecordset 클래스는 현재 레코드를 다른 위치로 바꿀 수 있는 다양한 멤버함수를 제공하며, 이를 이용하면 코든 레코드에 접근할 수 있다.
현재 레코드 변경 함수명 |
기능 |
MoveFirst() |
첫 번째 레코드를 현재 레코드로 설정한다. |
MoveLast() |
마지막 레코드를 현재 레코드로 설정한다. |
MoveNext() |
다음 위치의 레코드를 현재 레코드로 설정한다. |
MovePrev() |
이전 위치의 레코드를 현재 레코드로 설정한다. |
현재 레코드 위치 판단 함수 |
기능 |
IsBOF() |
현재 레코드가 첫 번째 레코드 바로 전 위치로 설정되었다면 TRUE를 리턴한다. 레코드가 전혀 없는 경우에도 TRUE를 리턴한다. |
IsEOF() |
현재 레코드가 마지막 레코드 바로 다음 위치로 설정되었다면 TRUE를 리턴한다. 레코드가 전혀 없는 경우에도 TRUE를 리턴한다. |
현재 레코드의 각 필드(Filed)를 출력하려면 다음과 같은 함수를 사용한다. ,
void CRecordset::GetFieldValue(short nIndex, CString& strValue);
nIndex
필드를 나타내는 인덱스(0 부터 시작) 다. MFC에서는 반드시 short 형변환해야 한다. (40행 참고)
strValue
해당 필드의 데이터가 저장된다.
4. 종료
레코드셋과 DB 객체 사용이 끝나면 각각 Close() 를 호출해야 한다. 두 객체 모두 Close() 를 호출하면 다른 레코드셋 이나 DB를 위해 객체를 재사용할 수 있다. 즉, 레코드셋 객체의 경우 Open()으로 얻은 레코드셋을 모두 사용했다면 Close()를 호출한 후 다시 Open()을 호출해서 새로운 레코드셋을 얻을 수 있다.