[SQLCE강좌] 7. RDA 아키텍쳐와 구현 방법


안녕 하세요.
멍구 입니다.

일곱번째 강좌 시작 하겠습니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

RDA 아키텍쳐

RDA란 원격지의 SQL서버에 접근해 원하는 작업을 수행하는 방법으로
경우에 따라서 SQLCE DB에 결과를 받아 오기도 합니다.
SELECT문장의 경우가 대표적인 경우로 MR과 마찮가지로 PDA에 저장된
SQLCE DB를 ADOCE로 접근해서 원하는 데이타를 볼 수 있습니다.

사용자 삽입 이미지

MR과 비슷하지만 Server쪽에 OLE 프로바이더를 통해 직접 SQL서버에
접근 하는 것을 볼 수있습니다. 그래서 MR과 같이 별도의 서버 셋팅이 필요가 없습니다.
그리고, 원하는 작업을 바로 SQL서버에 반영 할 수가 있습니다.
하지만 DB접근을 하기위해 항상 통신이 활성화 되어 있어야 하므로 엄청난 통신비를
부담해야 합니다.
지금이야 CDMA를 통한 통신이 주를 이루고 있어서 MR이 더 적합하지만,
곧 무선 인터넷이 활성화 될경우 RDA방식으로 개발을 해야 할 것입니다.

------------------------------------------------------------------

EVC로 MR구현

역시 EVC를 실행하고 프로젝트를 하나 생성합니다.

사용자 삽입 이미지

이번에는 RDANorthwind라는 프로젝트를 생성하겠습니다.

RDA역시 MR과 마찮가지로 SQLCE와 ADOCE를 사용하기 위한 사전 작업을 해줘야 합니다
※ 수행할 작업이 MR과 같으므로, 스크린 샷은 MR용을 재활용 하겠습니다.

사용자 삽입 이미지

첫번째로 [Alt]-[F7]키를 눌러 Project setting 다이얼로그를 띄워 Link탭을 열어,
Obejct/library modules란에 ca_mergex20.lib를 입력합니다.

사용자 삽입 이미지

두번째로 Tools / Options 의 Options 다이얼로그를 띄워 Directories탭을 열어,
Show directorys의 Include Files로 맞추고, ADOCE와 SQLCE의 해더파일 경로를 맞춰줍니다

사용자 삽입 이미지

마찮가지로 Show directorys의 Library Files로 맞추고, ADOCE와 SQLCE의
해더파일 경로를 맞춰줍니다. 이때는 해당 PDA의 CPU타잎에 알맞는 경로를
지정해야 합니다.

사용자 삽입 이미지


ADOCE의 편한 사용을 위해 MR에서 사용한 기존 클래스를 추가합니다

#include <ca_mergex20.h>
#include "VORecordset.h"


MRNorthwindDlg.cpp파일 윗부분에 SQLCE와 ADOCE를 사용하기 위해
include문을 추가 합니다.

사용자 삽입 이미지


리소스는 위 그림과 같이 구성합니다.
파란색 글씨는 해당 콘트롤의 ID입니다.

각 버튼의 클릭 이벤트 함수를 정의 합니다.
      afx_msg void OnBtnSelect();
afx_msg void OnBtnUpdate();

OnInitDialog()함수에 리스트롤 초기화 하는 코드를 삽입합니다.
	CListCtrl* pList = (CListCtrl*) GetDlgItem( IDC_LIST );
pList->InsertColumn( 0, L"ID", LVCFMT_CENTER, 50 );
pList->InsertColumn( 1, L"Name", LVCFMT_CENTER, 100 );

OnBtnSelect()함수에 Orders테이블을 SQL서버에서 조회해
List에 출력해주는 아래의 코드를 삽입한다.

	// 0. 각종 변수 초기화 
ISSCEEngine *pEngine;
ISSCERDA *pRda;
CVORecordset rs; // ADOCE 레코드셋
CString strSQL; // 쿼리 문장
CString strPath; // PDA 저장될 SQLCE DB 경로
CString strRemoteServer; // SQL서버 IP또는 이름
CString strDBName; // SQL서버에서 작업할 DB이름
CString strTblName; // SQLCE에 결과를 저장할 테이블이름
CString strLocalConnection; // SQLCE DB 연결 문자열
CString strRemoteConnection; // SQL서버 연결 문자열
CListCtrl* pList; // 리스트 콘트롤 포인터
CString str;
HRESULT hr;

pList = (CListCtrl*) GetDlgItem( IDC_LIST );

strSQL = L"SELECT OrderID, ShipName FROM Orders";
strPath = L"\\My Documents\\rNorthwind.sdf";
strRemoteServer = L"192.168.0.2";
strDBName = L"Northwind";
strTblName = L"Orders";
strLocalConnection.Format( L"Provider=microsoft.sqlserver.oledb.ce.2.0;Data Source=%s",
strPath );
strRemoteConnection.Format( L"provider=sqloledb; data source=%s; Initial Catalog=%s; user id=sa; password=sa",
strRemoteServer, strDBName );
// 1. 임시로 SQLCE DB파일을 생성한다.
hr = CoCreateInstance( CLSID_Engine, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCEEngine, (LPVOID*)&pEngine );
DeleteFile( strPath );
str.Format( _T("Data Source = %s;"), strPath );
hr = pEngine->CreateDatabase( (LPTSTR)(LPCTSTR)str );
pEngine->Release();
// 2. SQL서버에 접속해 SQLCE DB에 결과를 저장한다.
// COM사용을 위한 초기화
CoCreateInstance( CLSID_RemoteDataAccess, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCERDA, (LPVOID*)&pRda);
// 웹서버 접근을 위한 설정
pRda->put_InternetURL( L"http://192.168.0.2/Mobile/sscesa20.dll" );
pRda->put_InternetLogin( L"" );
pRda->put_InternetPassword( L"" );
// PDA저장
pRda->put_LocalConnectionString( (BSTR)(LPCTSTR)strLocalConnection );
pRda->Pull( (BSTR)(LPCTSTR)strTblName,
(BSTR)(LPCTSTR)strSQL,
(BSTR)(LPCTSTR)strRemoteConnection,
TRACKINGOFF_INDEXES, L"tblError" );
// 3. 저장된 결과를 List에 채운다.
hr = rs.Open( (BSTR)(LPCTSTR)strPath,
(BSTR)(LPCTSTR)strSQL,
adOpenKeyset, adLockOptimistic);

// 결과를 리스트에 넣는다.
pList->DeleteAllItems();
for (int i=0; !rs.IsEOF(); i++)
{
pList->InsertItem( i, rs.GetFieldValueString( 0 ) );
pList->SetItemText( i, 1, rs.GetFieldValueString(1) );

rs.MoveNext();
}

rs.Close();

1. 임시로 SQLCE DB파일을 생성한다. 부분에서는
기존의 Northwind.sdf파일을 삭제하고, ISSCEEngine 객체를 사용해
임시 DB파일을 생성합니다.

2. SQL서버에 접속해 SQLCE DB에 결과를 저장한다. 부분에서는
웹서버의 서버에이전트를 통해 oledb 프로바이더로 명령을 수행합니다.
이부분에서는 "SELECT" 쿼리를 사용하는데, 이렇게 테이블 형태의
작업 결과가 있는 명령을 실행할 경우 ISSCERDA 객체의 Pull 매소드를 사용합니다.
Pull 매소드를 사용해 작업 결과를 1번에서 생성한 임시DB에 저장합니다.

3. 저장된 결과를 List에 채운다. 부분에서는
PDA에 저장된 임시DB를 읽어 리스트에 채워주는 역활을 합니다.

OnBtnUpdate()함수에 아래 코드를 삽입합니다.

	// 0. 각종 변수 초기화 
ISSCERDA *pRda;
CString strSQL; // 쿼리 문장
CString strRemoteConnection; // SQL서버 연결 문자열
static CString strShipName; // 변경여부를 보여주기 위해

if ( !strShipName.Compare(L"newtype") )
strShipName = L"Vins et alcools Chevalier";
else
strShipName = L"newtype";

strSQL.Format( L"UPDATE Orders SET ShipName='%s' WHERE OrderID=10248",
strShipName );
strRemoteConnection=L"provider=sqloledb; data source=192.168.0.2; Initial Catalog=Northwind; user id=sa; password=sa";
// 1. SQL서버에 접속해 명령을 실행한다.
// COM사용을 위한 초기화
CoCreateInstance( CLSID_RemoteDataAccess, NULL, CLSCTX_INPROC_SERVER,
IID_ISSCERDA, (LPVOID*)&pRda);
// 웹서버 접근을 위한 설정
pRda->put_InternetURL( L"http://192.168.0.2/Mobile/sscesa20.dll" );
pRda->put_InternetLogin( L"" );
pRda->put_InternetPassword( L"" );

// PDA저장
pRda->SubmitSQL( (LPTSTR)(LPCTSTR)strSQL, (LPTSTR)(LPCTSTR)strRemoteConnection );

여기서는 OrderID가 10248인 첫번째 레코드의 ShipName을 바꾸는 역활을 합니다.
결과가 없는 Update쿼리문이므로 ADOCE를 사용할 필요가 없습니다.
이렇게 결과가 없는 쿼리문은 Push나 SubmitSQL 매소드를 사용합니다.
제가 이해한 Push와 SubmitSQL의 차이점은 Push의 경우 Pull로 가져온 SQLCE DB를
사용해 명령을 수행 한다는 것이고, SubmitSQL은 SQL서버에 직접 명령을 수행하는
것으로 이해 했습니다. 그렇다 보니 Push가 조금은 더 사용이 번거롭네요.
틀린점이나 부족한 점이 있으면
지적해주세요


이제 예제를 실행 해보겠습니다.

사용자 삽입 이미지


처음 실행했을때 그림 입니다. 별로 볼것 없네요..^^;


사용자 삽입 이미지


[Select]버튼을 눌렀을때 그림입니다.
이 결과는 버튼이 눌렸을때, 서버에서 결과를 받아 잠시 PDA에 저장하여
리스트에 뿌려주는 것입니다.
여기서 첫번째 래코드의 ShipName이 "Vins et alcools Chevalier"인것을 확인할 수 있습니다.


사용자 삽입 이미지


위의 그림은 [Update]버튼을 누르고, 잠시후에
[Select]버튼을 눌렀을때 그림입니다.
보시면 첫번째 래코드의 ShipName이 바뀐것을 알 수 있습니다.


강좌의 전체 소스 받고 싶으시면
를 클릭하세요



이상으로 RDA의 설명이 끝났습니다.
RDA는 작업 결과를 확인할 필요가 있을경우 MR보다 사용이 더 복잡한 것을
알 수 있습니다. RDA 역시 제가 조금은 쉽게 사용할 수 있도록 클래스를 만들 었습니다.
아래의 파일을 다른 이름으로 대상 저장 하여 사용 하시면 됩니다.


RDANorthwind2.exe

업데이트된 전체


-----------------------------------------------------------------

덧글

RDA에서 Stored Procedure의 사용

RDA의 경우 SQL서버에 직접 접근하는 방식이기 때문에 Stored Procedure(이하 SP)를
사용할 수가 있습니다.

strSQL.Format( _T("EXEC p_UserLoginEx '%s', '%s'"), strUserID, strPwd );

위와 같은 코드면 간단하게 사용이 가능 합니다.
단, 주의 해야할 점은 Output Parameter를 사용할 수 없다는 점 입니다.
꼭 결과를 보고자 할때는 Select문을 사용해 Pull로 보여주어야 합니다.
이때 ISNULL로 Null값을 없애고, AS 구분을 사용해 반드시 필드명을 지정해
주어야 합니다.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

이상으로 일곱번째 강좌를 마칩니다.

휴 이제야 강좌가 끝이 났네요.
부디 도움이 되었으면 좋겠네요.

정말 수고하셨습니다..^^