Delphi 5 Contact 중의 "5부 분산 환경 솔루션/38장 마이다스의 활용 (1439 페이지)"
내용을 적용하던중에 의문사항이 있어서 씁니다.
같은 내용을 저자인 박준후님에게 메일을 보냈으나 답변이 늦어지는군요... --;
클라이언트에는 TSocketConnection 1개와 TClientDataSet 1개를,
서버의 리모트데이타모듈에는 TQuery 1개와 TDataSetProvier1개,
그리고 TUpdateSQL 2개(Master용과 Detail용)를 올려놨습니다.
근데, 문제는 마스터는 저장이 되는데 디테일 저장시에
OnReconcilError 에서 찍어보니 디테일 테이블의 컬럼에서 Constraint 가 발생네요...
혹시 같은 작업을 해 보신분이나 제가 한 작업에 관심있으신분은
아래 소스를 보시고 조언 바랍니다.
의도는 서버의 리모트데이탑모듈이 Create 될때, UpdateSQL 의 SQL 구문을
자동생성하여 조인된 테이블을 업데이트하는 겁니다.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Client 모듈에서 데이타를 추가한후 ApplyUpdate 를 날리면
Master 는 저장이 되지만 다음과 같은 에러가 발생합니다.
Field value required.
ORA-01400: cannot insert NULL into ("ICM"."AGRMAST"."AMSUCODE")
마스터 테이블에는 값이 제대로 들어 갔는데 디테일 테이블에 값이 할당 되지 않는것 같군요
왜 마스터에 저장된 키값으로 디테일까지 저장이 안되는지 알고 싶습니다.
물론
다음은 제가 구현한 부분입니다. 보시고 잘못된 곳이 있다면 알려 주십시오
0. 환경
툴 : 델5
DB : Oracle 8.0.5
DB 서버 : Linux
App 서버 : NT40
Client : Win98
1. 클라이언트
(1) 콤포넌트
sccnAgree : TSocketConnection
cdstMaster : TClientDataSet
(2) 코딩
< SQL 구문 >
SELECT AGRMAST.*, AGRDETAIL.*,
ITEM.INAME,
STITEM.STNAME,
STITEM.STUNIT
FROM AGRMAST, AGRDETAIL, STITEM, ITEM
WHERE AGRMAST.SITE_MEMBER_MID = AGRDETAIL.AGRMAST_SITE_MEMBER_MID AND
AGRMAST.SITE_SCODE = AGRDETAIL.AGRMAST_SITE_SCODE AND
AGRMAST.AMSUCODE = AGRDETAIL.AGRMAST_AMSUCODE AND
AGRDETAIL.AGRMAST_SITE_MEMBER_MID = :P_MID AND
AGRDETAIL.AGRMAST_SITE_SCODE = :P_SCODE AND
AGRDETAIL.AGRMAST_AMSUCODE = :P_SUCODE AND
AGRDETAIL.STITEM_STCODE = STITEM.STCODE AND
AGRDETAIL.ADICODE = ITEM.ICODE
ORDER BY AGRDETAIL.ADSEQ, STITEM.STNAME, STITEM.STUNIT, AGRDETAIL.ADICODE, ITEM.INAME
2. 서버
(1) 콤포넌트
qryAgree : TQuery
usqlMaster : TUpdateSQL
usqlDetail : TUpdateSQL
(2) 코딩
procedure TrdmIAgree.dstpIAgreeBeforeUpdateRecord(Sender: TObject; // 책에 있는 루틴
SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
usqlMaster.DataSet := qryIAgree;
usqlDetail.DataSet := qryIAgree;
pSetUsqlParams(usqlMaster.Query[UpdateKind], DeltaDS);
usqlMaster.ExecSQL(UpdateKind);
pSetUsqlParams(usqlDetail.Query[UpdateKind], DeltaDS);
usqlDetail.ExecSQL(UpdateKind);
Applied := True;
end;
procedure TrdmIAgree.pSetUSQLParams(qrySet : TQuery; cdstSet : TClientDataSet); // 책에 있는 루틴
var
liI : Integer;
lbOld : Boolean;
loParam : TParam;
lsParamName : String;
loField : TField;
lvValue : Variant;
begin
with qrySet do
begin
for liI := 0 to Params.Count-1 do
begin
loParam := Params[liI];
lsParamName := loParam.Name;
lbOld := CompareText(Copy(lsParamName, 1, 4), 'OLD_') = 0;
if lbOld then
System.Delete(lsParamName, 1, 4);
loField := cdstSet.FindField(lsParamName);
if not Assigned(loField) then
Continue;
if lbOld then
loParam.AssignFieldValue(loField, loField.OldValue)
else
begin
lvValue := loField.NewValue;
if VarIsEmpty(lvValue) then
lvValue := loField.OldValue;
loParam.AssignFieldValue(loField, lvValue);
end;
end;
end;
end;
procedure TrdmIAgree.pSetUsqlSQL(usqlSet : TUpdateSQL; lsTableName : String; lasFields, lasKeyFields : array of String);
// 넘겨진 UpdateSQL 의 ModifySQL, InsertSQL, DeleteSQL 을 생성합니다.
// 디자인시에는 TQuery 의 필드 및 TUpdateSQL 에 관한 내용이 전혀 없습니다.
var
liI : Integer;
lsModifySQL, lsInsertSQL, lsDeleteSQL : String;
begin
with usqlSet do
begin
lsModifySQL := 'UPDATE ' + lsTableName + #13 +
'SET' + #13;
for liI := Low(lasFields) to High(lasFields)-1 do
begin
lsModifySQL := lsModifySQL + lasFields[liI] + ' = :' + lasFields[liI] + ',' + #13;
end;
lsModifySQL := lsModifySQL + lasFields[High(lasFields)] + ' = :' + lasFields[High(lasFields)] + #13;
lsModifySQL := lsModifySQL + 'WHERE' + #13;
for liI := Low(lasKeyFields) to High(lasKeyFields)-1 do
begin
lsModifySQL := lsModifySQL + lasKeyFields[liI] + ' = :OLD_' + lasKeyFields[liI] + ' AND' + #13;
end;
lsModifySQL := lsModifySQL + lasKeyFields[High(lasKeyFields)] + ' = :OLD_' + lasKeyFields[High(lasKeyFields)] + #13;
lsInsertSQL := 'INSERT INTO ' + lsTableName + #13 +
'(';
for liI := Low(lasFields) to High(lasFields)-1 do
begin
lsInsertSQL := lsInsertSQL + lasFields[liI] + ',' + #13;
end;
lsInsertSQL := lsInsertSQL + lasFields[High(lasFields)] + ')' + #13 +
'VALUES' + #13 +
'(';
for liI := Low(lasFields) to High(lasFields)-1 do
begin
lsInsertSQL := lsInsertSQL + ':' + lasFields[liI] + ',' + #13;
end;
lsInsertSQL := lsInsertSQL + ':' + lasFields[High(lasFields)] + ')';
lsDeleteSQL := 'DELETE FROM ' + lsTableName + #13 +
'WHERE' + #13;
for liI := Low(lasKeyFields) to High(lasKeyFields)-1 do
begin
lsDeleteSQL := lsDeleteSQL + lasKeyFields[liI] + ' = :OLD_' + lasKeyFields[liI] + ' AND' + #13;
end;
lsDeleteSQL := lsDeleteSQL + lasKeyFields[High(lasKeyFields)] + ' = :OLD_' + lasKeyFields[High(lasKeyFields)];
ModifySQL.Clear();
ModifySQL.Add(lsModifySQL);
InsertSQL.Clear();
InsertSQL.Add(lsInsertSQL);
DeleteSQL.Clear();
DeleteSQL.Add(lsDeleteSQL);
end;
end;
procedure TrdmIAgree.RemoteDataModuleCreate(Sender: TObject); // 리모트 데이타 모듈이 생성될때 TUpdateSQL 의 SQL 구문 생성
begin
pSetUsqlSQL(usqlMaster,
'AGRMAST',
[ 'SITE_MEMBER_MID',
'SITE_SCODE',
'AMSUCODE',
'AMKUMK'],
[ 'SITE_MEMBER_MID',
'SITE_SCODE',
'AMSUCODE']);
pSetUsqlSQL(usqlDetail,
'AGRDETAIL',
[ 'AGRMAST_SITE_MEMBER_MID',
'AGRMAST_SITE_SCODE',
'AGRMAST_AMSUCODE',
'ADSEQ',
'STITEM_STCODE',
'ADICODE',
'ADSNO',
'ADQTY',
'ADUPR',
'ADKUMK'],
[
'AGRMAST_SITE_MEMBER_MID',
'AGRMAST_SITE_SCODE',
'AGRMAST_AMSUCODE']);
end;
다음은 위의 모듈을 클라이언트에서 실행하여 시뮬해본 결과 생성된 SQL 구문입니다.
{
===============================마스터
UPDATE AGRMAST
SET
SITE_MEMBER_MID = :SITE_MEMBER_MID,
SITE_SCODE = :SITE_SCODE,
AMSUCODE = :AMSUCODE,
AMKUMK = :AMKUMK
WHERE
SITE_MEMBER_MID = :OLD_SITE_MEMBER_MID AND
SITE_SCODE = :OLD_SITE_SCODE AND
AMSUCODE = :OLD_AMSUCODE
INSERT INTO AGRMAST
(SITE_MEMBER_MID,
SITE_SCODE,
AMSUCODE,
AMKUMK)
VALUES
(:SITE_MEMBER_MID,
:SITE_SCODE,
:AMSUCODE,
:AMKUMK)
DELETE FROM AGRMAST
WHERE
SITE_MEMBER_MID = :OLD_SITE_MEMBER_MID AND
SITE_SCODE = :OLD_SITE_SCODE AND
AMSUCODE = :OLD_AMSUCODE
===============================================디테일
UPDATE AGRDETAIL
SET
AGRMAST_SITE_MEMBER_MID = :AGRMAST_SITE_MEMBER_MID,
AGRMAST_SITE_SCODE = :AGRMAST_SITE_SCODE,
AGRMAST_AMSUCODE = :AGRMAST_AMSUCODE,
ADSEQ = :ADSEQ,
STITEM_STCODE = :STITEM_STCODE,
ADICODE = :ADICODE,
ADSNO = :ADSNO,
ADQTY = :ADQTY,
ADUPR = :ADUPR,
ADKUMK = :ADKUMK
WHERE
AGRMAST_SITE_MEMBER_MID = :OLD_AGRMAST_SITE_MEMBER_MID AND
AGRMAST_SITE_SCODE = :OLD_AGRMAST_SITE_SCODE AND
AGRMAST_AMSUCODE = :OLD_AGRMAST_AMSUCODE
INSERT INTO AGRDETAIL
(AGRMAST_SITE_MEMBER_MID,
AGRMAST_SITE_SCODE,
AGRMAST_AMSUCODE,
ADSEQ,
STITEM_STCODE,
ADICODE,
ADSNO,
ADQTY,
ADUPR,
ADKUMK)
VALUES
(:AGRMAST_SITE_MEMBER_MID,
:AGRMAST_SITE_SCODE,
:AGRMAST_AMSUCODE,
:ADSEQ,
:STITEM_STCODE,
:ADICODE,
:ADSNO,
:ADQTY,
:ADUPR,
:ADKUMK)
DELETE FROM AGRDETAIL
WHERE
AGRMAST_SITE_MEMBER_MID = :OLD_AGRMAST_SITE_MEMBER_MID AND
AGRMAST_SITE_SCODE = :OLD_AGRMAST_SITE_SCODE AND
AGRMAST_AMSUCODE = :OLD_AGRMAST_AMSUCODE
}