处理是指数据层面而非业务层面.是业务处理,接口交换(dd,dxi_change_log等)功能的基础.
单据交换系统实际上是一个业务数据对象的同步子系统.业务系统在其上构建.
同步是基于对象的变更进行的。
对象分为单表对象,多表对象.单表对象对应表的一条记录,如商品资料.多表对象是关系型对象,关系的逻辑结构呈现树型.
多表对象是每对象分别处理(抽取,传输,接收处理),单表支持组合以提高效率.
关于dxi变更类型的概念,见http://blog.csdn.net/wherwh/article/details/41467363.
这里仅对多表对象的单据几种变更的处理实现进行说明。
.新增:
执行插入,事务过程中如果有主键冲突,则根据主键冲突处理规则(忽略,重试,更新)决定后续动作。
更新采用全量修改。
.全量修改:
先删除对象记录,然后执行插入。
.增量修改:
只对从表而言。主表主键冲突时,忽略此错误继续处理从表。
从表按insert处理,如果出现主键冲突,则失败.
(gyb已经增加了支持从表冲突时更新,该实现需要优化,方向之一是不能因此放弃数据库批插入的特性而影响性能)
增量修改适用于补明细数据的情形.
接口方式:
.dd:直接面向数据源的接口方式,只能支持有限的变更类型.如无法支持删除.
删除对应的业务术语可能是废除,取消,可以通过局部修改(OM_SLIGHT_CHANGE)实现.
.dxi_chaneg_log:基于变更的接口方式,包含变更类型信息.可支持所有的变更类型,但目前有的因没有需求而未实现.
对于dd,从das 2.0开始可以在抽取规则中指定变更类型,支持新增,增量修改,全表更新(仅对单表).全表更新是das 2.0新增的.
变更类型在抽取规则的<ChangeMode>配置.默认未指定.
此前的系统包括gyb和das,只支持新增和增量修改.
增量修改是由抽取规则的f023n_5606指定的,f023n_5606=1表示增量处理,对应变更类型为OM_INC,否则为OM_NEW.默认非增量方式.
为保证兼容性,das 2.0先检查f023n_5606的值,如果配置了<ChangeMode>,则覆盖f023n_5606的设置.
以下代码反映了上面3种变更的处理差异.
单据数据处理入口函数
int CSheet::ProcessImport(CMsg *msg,CSheetDealResult &sdr) {
USEDBC(pdbor,this->dbc_name_.c_str());
int result = 0;
switch(op_type_) {
case OM_UNSPECIFED:
case OM_NEW:
result = HandleNew(msg,sdr);
break;
case OM_DELETE:
result = HandleDelete(msg,sdr);
break;
case OM_OVERWRITE:
result = HandleUpdate(msg,sdr);
break;
case OM_PK_CHANGED:
result = HandlePKChange(msg,sdr);
break;
case OM_INC:
result = HandleIncChange(msg,sdr,1); ///< @note 第3个参数为flag=1,表示忽略主键冲突
break;
case OM_SLIGHT_CHANGE:
result = HandleSlightChange(msg,sdr);
break;
case OM_FULL_SYNC:
result = HandleFullSync(msg,sdr);
break;
}
return result;
}
新增处理函数
int CSheet::HandleNew(CMsg *msg,CSheetDealResult &sdr) {
USEDBC(pdbor,this->dbc_name_.c_str());
FAIL_RETURN(pdbor->BeginTrans(),,-1);
if (HandleIncChange(msg,sdr,0)) { ///< @note 第3个参数为flag=0,表示不忽略主键冲突
pdbor->RollbackTrans();
return -2;
}
FAIL_RETURN(pdbor->CommitTrans(),,-1);
return 0;
}
全量修改处理函数
int CSheet::HandleUpdate(CMsg *msg,CSheetDealResult &sdr) {
USEDBC(pdbor,this->dbc_name_.c_str());
FAIL_RETURN(pdbor->BeginTrans(),,-1);
if (HandleDelete(msg,sdr)) {
pdbor->RollbackTrans();
return -1;
}
if (Insert_v2(msg,1)) {
pdbor->RollbackTrans();
return -1;
}
FAIL_RETURN(pdbor->CommitTrans(),,-1);
return 0;
}
int CMultiSheet::HandleIncChange(CMsg *msg,CSheetDealResult &sdr,int flag) {
USEDBC(pdbor,this->dbc_name_.c_str());
FAIL_RETURN(pdbor->BeginTrans(),,-1);
if (Insert_v2(msg,flag)) {
if ((last_errno_!=CE_PRIKEY_REPEAT)||(on_dup_key_==2)) {
pdbor->RollbackTrans();
return -2;
}
if (on_dup_key_==1) {
sdr.result_ = 1; ///< 忽略此错误
pdbor->RollbackTrans();
return -3;
}
else { ///< 3: 按更新处理:先删除再insert
if (HandleDelete(msg,sdr)) {
pdbor->RollbackTrans();
return -3;
}
if (Insert_v2(msg,0)) {
pdbor->RollbackTrans();
return -4;
}
}
}
FAIL_RETURN(pdbor->CommitTrans(),,-1);
return 0;
}
Insert_v2有非通用单据处理实现的痕迹,实现上有优化空间.所有以“_2"作为名字后缀的函数都是如此安排的.
(单据处理实现经历了无类型,类体系,通用处理3个阶段,每次过渡都需要考虑兼容,导致修改的不彻底)
int CMultiSheet::Insert_v2(CMsg *msg,short flag) {
CRowset *rs1 = msg->GetRowset(0);
assert(rs1!=0);
unsigned long rec_count = rs1->GetRSMeta(RST_ROW_CNT);
assert(rec_count==1);
const char *sheet_id_name = env_->GetSysFieldMgr()->GetField(SF_SHEET_ID);
if (rs1->HasField(sheet_id_name)) {
this->master_->sheet_id_ = rs1->GetFieldValueByName(0,sheet_id_name);
}
int result = 0;
CPreSQLInfo psi;
if (PrepareSQL_v2(&psi,rs1,0))
return -1;
string sql = LogMsg("insert into %s(%s) values(%s)", this->sheet_type_info_->master_tbl_name(),psi.fld_list_.c_str(), psi.val_list_.c_str());
USEDBC(pdbor,this->dbc_name_.c_str());
FAIL_RETURN(pdbor->BeginTrans(),,-1);
bool br = pdbor->Execute(adCmdText, sql.c_str());
if (!br) {
DWORD last_error = pdbor->GetLastErrorCode();
if (pdbor->GetDBExt()->IsDuplicateKeyError(last_error)) {
last_errno_ = CE_PRIKEY_REPEAT;
}
///< 如果非主键冲突错误且不能忽略主表主键冲突,则失败返回
if (flag==0||last_errno_!=CE_PRIKEY_REPEAT) {
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_ERROR,"数据导入(单据类型:%d,单据编号:%s)保存数据时执行%s失败.错误:%s.\n",
this->sheet_type_info_->type(),this->GetSheetID(), sql.c_str(),pdbor->GetLastError());
pdbor->RollbackTrans();
return -1;
}
last_errno_ = 0; ///< 重置错误码
/// 继续
/// 后续操作成功仍可提交
}
unsigned short detail_num = this->sheet_type_info_->get_tbl_num()-1;
for (unsigned short detail_idx=0;detail_idx<detail_num;detail_idx++) {
CRowset *prs = msg->GetRowset(1+detail_idx);
unsigned long rec_count = prs->GetRSMeta(RST_ROW_CNT);
CLargeStringArray vs;
CPreSQLInfo psi;
for (unsigned int i = 0; i < rec_count; i++) {
psi.Reset();
if (PrepareDetailSQL_v2(&psi,prs,i,detail_idx)) {
pdbor->RollbackTrans();
return -3;
}
char *buffer = 0;
FormatString(2048,&buffer,"%s",psi.val_list_.c_str());
vs.Add(buffer);
}
if (pdbor->GetDBExt()->BatchInsert(this->sheet_type_info_->get_tbl_name(detail_idx+1),psi.fld_list_.c_str(),vs,BATCH_INSERT_NUM)) {
DWORD last_error = pdbor->GetLastErrorCode();
if (pdbor->GetDBExt()->IsDuplicateKeyError(last_error)) {
last_errno_ = CE_PRIKEY_REPEAT;
}
if (flag==0||last_errno_!=CE_PRIKEY_REPEAT) {
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_ERROR,"CDualSheet::Save失败,错误:%s.\n",pdbor->GetLastError());
pdbor->RollbackTrans();
return -1;
}
else ///< 从表也允许忽略主键冲突
last_errno_ = 0;
}
}
FAIL_RETURN(pdbor->CommitTrans(),,-1);
return 0;
}