OLE-DB 는 ODBC 보다 훨씬 진보된 개념으로 매우 강력한 데이터베이스를 구축할 수 있게 해주는데, OLE-DB는 텍스트뿐만 아니라 이미지까지도 포함시킬 수 있다. OLE-DB는 SQL 을 지원하지 않는 데이터베이스까지 확장한 개념이며, 이를 구현하고자 COM을 사용한다. OLE-DB는 내부적으로 공급자와 소비자로 나뉘며, 공급자는 데이터베이스 제작사에서 ODBC 드라이버를 제공하듯 제작사가 제공하는 인터페이스이고, 소비자는 제공받은 인터페이스를 활용하여 실제로 이비를 사용하는 인터페이스이다. OLE-DB 코드는 각종 템플릿 클래스들로 구성되어 있으며 모두 ATL(Active X Template Library) 이다. 그러므로 코드를 작성할 때 객체화된 클래스가 어떤 역활을 하는지 이해해야 한다.
COleDBRecordView::OnInitialUpdate();
if(m_List.GetHeaderCtrl()->GetItemCount() <= 0)
{
m_List.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 100);
m_List.InsertColumn(1, _T("Phone Number"), LVCFMT_LEFT, 150);
m_List.InsertColumn(2, _T("Address"), LVCFMT_LEFT, 200);
m_List.InsertColumn(3, _T("Age"), LVCFMT_LEFT, 80);
}
ListupAllRecords();
}
// COleDBDemo2View 메시지 처리기
void COleDBDemo2View::ListupAllRecords(void)
{
CString strTmp=_T("");
m_pSet->MoveFirst();
do{
m_List.InsertItem(0, m_pSet->m_Name, 0);
m_List.SetItemText(0,1, m_pSet->m_PhoneNumber);
m_List.SetItemText(0,2, m_pSet->m_Address);
strTmp.Format(_T("%ld"), m_pSet->m_Age);
m_List.SetItemText(0,3, strTmp);
// movenext는 레코드가 있으면 S_OK 를, 없으면 DB_S_ENDOFROWSET 을 반환한다.
}while(m_pSet->MoveNext() != DB_S_ENDOFROWSET);
m_pSet->MoveFirst();
}
// 다음은 레코드를 추가하기 위해 DB 속성을 변경한다.
void GetRowsetProperties(CDBPropSet* pPropSet) //Set.h
{
//DB 에 속성을 추가함,
//1번째 인자는 속성의 인덱스값으로 추가한 DBPROP_UPDATABLITY 속성은 디비 접근권한과 관련이 있다.
//2번째 인자는 새 로크드를 추가,갱신, 지우는 권한이며 사실상 모든 권한을 준 것이다.
pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
}
class COleDBDemo2Set : public CCommand<CAccessor<COleDBDemo2SetAccessor> >
{ //CCommand 부분에 올래는 CTable 이었음. CTable 클래스를 이용하면 레코드를 단순히 보는 것에 그치지만
//CCommand 를 쓰면 SQL 문을 실행할 수 있기 때문이다.
public:
HRESULT OpenAll()
{
HRESULT hr;
hr = OpenDataSource();
if (FAILED(hr))
return hr;
~~~
}
HRESULT OpenRowset(DBPROPSET *pPropSet = NULL)
{
// HRESULT hr = Open(m_session, L"UserData", pPropSet); // CTable의 Open메서드였는데 단순히 테이블을 연다.
CString strSQL = _T("Select * from UserData");
// CCommand의 Open() 메서드는 첫번째 인자(세션)에 대해 세번째 인자로 주어진 권한으로 SQL 문을 실행함.
HRESULT hr = Open(m_session, strSQL, pPropSet);
~~
}
그 밑에다가 다음처럼 RunSQL() 멤버 함수를 선언하고 작성한다.
//OpenRowset() 함수와 같지만, 새로운 SQL 문을 실행하기에 앞서 CCommand::Close(),
//ReleaseCommand()를 호출하여 앞서 실행한 SQL 문의 결과를 모두 정리한다.
//내부적으로 Close() 함수는 관련된 레코드(Rowset)를 해제하고, ReleaseCommand()는 관련된 접근 지정자를 해제한다.
HRESULT RunSQL(CString strSQL, DBPROPSET* pPropSet = NULL)
{
Close();
ReleaseCommand();
HRESULT hr = Open(m_session, strSQL, pPropSet);
return hr;
}
// 다음은 레코드를 추가하는 코드다.
void COleDBDemo2View::OnBnClickedButtonAddnew()
{
CCommand<CDynamicAccessor> Cmd;
//CDynamicAccessor, 여러 접근 지정자 객체 중 하나로, 테이블의 구조를 모르는 상황에서 사용가능.
//이 접근 지정자를 이용해 디비 접근시 어떤 권한을 갖도록 할 것인지 속성설정.
CDBPropSet propset(DBPROPSET_ROWSET);
propset.AddProperty(DBPROP_IRowsetChange, true);
propset.AddProperty(DBPROP_UPDATABILITY,DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT);
//이 속성을 가지고 현재 연결된 세션에서 SQL문을 실행한다.
HRESULT hr = Cmd.Open(m_pSet->m_session, _T("select * from UserData"), &propset);
if(FAILED(hr))
{
AfxMessageBox(_T("ERROR : Failed to execute SQL"));
}
TCHAR *pszValue = NULL;
//CDynamicAccessor::SetStatus() ,현재 레코드의 특정 필드의 상태를 지정한다.
//DBSTATUS_S_IGNORE 은 필드를 무시하라는 의미. 왜냐면 1번 필드(컬럼)이 일련번호(autoincrement)값이기 때문.
Cmd.SetStatus(1, DBSTATUS_S_IGNORE);
//GetValue() 는 필드값이 저장된 버퍼의 주소를 void형 포인터로 반환하며,
//이 주소가 가리키는 메모리에 새 레코드의 필드 값을 써주고,
//SetLength()로 길이(바이트 단위)를 지정한 후,
//SetStatus() 메서드를 호출해 제대로 값이 쓰였다고 지정하면 한 필드에 대한 값의 설정이 완료된다.
pszValue = (TCHAR*) Cmd.GetValue(2);
wsprintf(pszValue, _T("%s"), _T("김성민"));
Cmd.SetLength(2,lstrlen(pszValue)*2);
//BSTATUS_S_OK 를 사용하면 SetLength() 에서 지정한 길이만큼 소비자 버퍼에 값을 설정하라는 의미
Cmd.SetStatus(2, DBSTATUS_S_OK);
pszValue = (TCHAR*) Cmd.GetValue(3);
wsprintf(pszValue, _T("%s"), _T("02-2222-1111"));
Cmd.SetLength(3,lstrlen(pszValue)*2);
Cmd.SetStatus(3, DBSTATUS_S_OK);
pszValue = (TCHAR*) Cmd.GetValue(4);
wsprintf(pszValue, _T("%s"), _T("서울시 강서구 화곡동"));
Cmd.SetLength(4,lstrlen(pszValue)*2);
Cmd.SetStatus(4, DBSTATUS_S_OK);
ULONG* plAge = (ULONG*) Cmd.GetValue(5);
*plAge = 100;
Cmd.SetLength(5,sizeof(ULONG));
Cmd.SetStatus(5, DBSTATUS_S_OK);
//이렇게 레코드 입력받는 것을 하드 코딩하였는데, 제대로 하려면 사용자로부터 입력을 받아야 한다.
//구현해볼것.
hr = Cmd.Insert(); //CRowset::Insert() , 새 레코드를 만들어 테이블에 추가한다.
if(FAILED(hr))
{
AfxMessageBox(_T("ERROR : Failed to insert new record"));
}
Cmd.Close();
Cmd.ReleaseCommand();
if(m_List.GetItemCount() > 0) m_List.DeleteAllItems();
m_pSet->RunSQL(_T("select * from UserData"));
ListupAllRecords();
}
//다음은 레코드를 수정, 삭제하는 코드다.
void COleDBDemo2View::OnBnClickedButtonModifyrecord()
{
CCommand<CDynamicAccessor> Cmd; //CDynamicAccessor, 여러 접근 지정자 객체 중 하나로, 테이블의 구조를 모르는 상황에서 사용가능.
//이 접근 지정자를 이용해 디비 접근시 어떤 권한을 갖도록 할 것인지 속성설정.
CDBPropSet propset(DBPROPSET_ROWSET);
propset.AddProperty(DBPROP_IRowsetChange, true);
propset.AddProperty(DBPROP_UPDATABILITY,DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT);
//where문으로 조건을 걸어 결과가 하나만 나오게 하였다.
HRESULT hr = Cmd.Open(m_pSet->m_session, _T("select * from UserData where Name='김성민'"), &propset);
if(FAILED(hr))
{
AfxMessageBox(_T("ERROR : Failed to execute SQL"));
}
//MoveFirst() 로 하나나온 레코드에 접근한다.
Cmd.MoveFirst();
TCHAR *pszValue = NULL;
//CDynamicAccessor::SetStatus() ,현재 레코드의 특정 필드의 상태를 지정한다.
//DBSTATUS_S_IGNORE 은 필드를 무시하라는 의미. 왜냐면 1번 필드(컬럼)이 일련번호(autoincrement)값이기 때문.
Cmd.SetStatus(1, DBSTATUS_S_IGNORE);
//나이에 접근하여 값을 변경한다.
ULONG* plAge = (ULONG*) Cmd.GetValue(5);
*plAge = 999999;
Cmd.SetLength(5,sizeof(ULONG));
Cmd.SetStatus(5, DBSTATUS_S_OK);
//CRowset::SetData() 함수로 DBSTATUS_S_OK 인 필드의 값을 수정한다.
hr = Cmd.SetData(); //삭제할때는 Cmd.Delete(); 으로만 바꿔주면 된다.
if(FAILED(hr))
{
AfxMessageBox(_T("ERROR : Failed to modify record"));
}
Cmd.Update(); //실제 디비 테이블에 결과가 반영되도록 한다.
Cmd.Close();
Cmd.ReleaseCommand();
if(m_List.GetItemCount() > 0) m_List.DeleteAllItems();
m_pSet->RunSQL(_T("select * from UserData"));
ListupAllRecords();
}