对一个数据库表进行增,删,修改,查询操作是常见的编程任务.
统一和简化此类开发任务,可以提高开发效率,包括实现,调试和维护.
本文以常购商品的功能为例,说明基于需求完整实现的过程.
提供门店经常采购商品信息的管理,提供对快捷订单的支持.
一个协议的功能单一化,避免让一个协议承载太多功能,协议与用例一一对应作为设计原则。
常购商品的4协议如下:
.新增常购商品(7293)
支持批量新增
.修改常购商品(7294)
支持批量修改
.删除常购场频(7295)
支持批量删除
.查询常购商品(7296)
常购商品数据维护的协议都设计为支持批量操作.
并非所有操作都要支持批量,取决于交互操作界面规定的方式,批量处理一般对应一个列表界面,记录行有一个勾选框。
常见的批量操作有删除,状态改变。
新增和修改并不总是要求批量操作。
以下内容在设计阶段规定:
.相关表结构:本例有t_Ven_PreferGoods
.协议在哪个插件中实现:本例为wiser插件
.新增哪些类,类的头文件,实现文件(cpp):如果可被其它插件访问则把文件放到common目录下.
*协议设计时要求协议的参数和行集列名称与数据库表一致.
常购商品表(t_Ven_PreferGoods)结构:
EId int not null comment '购买者企业id', GoodsID int not null comment '商品id', CoEId int not null comment '供货商企业id', PackQty int not null comment '常购件数'
CPreferGoods类定义:在wiser_data.h
#include "slic_data_env.h" #include "orm_base.h" // ///< 常购商品 class CPreferGoods : public CORMBase { public: class CQueryCond { ///< 常购商品查询条件 public: string key_value_; }; public: SLIC_EID eid_; ///< 购买者企业ID SLIC_GOODS_ID goods_id_; ///< 商品ID SLIC_EID co_eid_; ///< 供货者企业ID int pack_qty_; ///< 常购数量(件数) public: CPreferGoods():eid_(0),goods_id_(0),co_eid_(0),pack_qty_(0) { } BIND_DEFINE(CPreferGoods); static int Query(CQueryCond &cond,vector<CPreferGoods*> &data); ///< 查询常购商品 };
CPreferGoods从CORMBase派生.
插件工程中加入common\orm_base.cpp.
增加CPreferGoods绑定(wiser_data.cpp)
/ BIND_DECLARE_BEGIN(CPreferGoods,"t_Ven_PreferGoods") FIELD_BIND3(CPreferGoods,eid_,"EId",true), FIELD_BIND3(CPreferGoods,co_eid_,"CoEId",true), FIELD_BIND3(CPreferGoods,goods_id_,"GoodsID",true), FIELD_BIND1(CPreferGoods,pack_qty_,"PackQty"), BIND_DECLARE_END(CPreferGoods) / int CPreferGoods::Query(CQueryCond &cond,vector<CPreferGoods*> &data) { return CE_UNIMPLEMENT; ///< 未实现 }
未实现CPreferGoods::Query的原因是,协议7296处理函数利用了SQLBasedMsgProc直接从SQL查询返回消息包,不需要生成对象的过程,更有效.
如果查询后还需要后续处理,则采用对象化方式更合适.
在插件Initialize中增加初始化绑定(CPreferGoods::InitBinder)
int CWiser::Initialize() { parent::Initialize(); CDataEnv::Init(); INIT_DATAENV_OBJECT(CDataEnv::env_,this->local_dbc_); CPreferGoods::InitBinder(); }
在插件类中(wiser.h文件中)增加协议处理函数定义.
class CWiser : public CBasePlugin { ///< 查询常购商品(7293) int OnQueryPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or); ///< 新增操作常购商品(7294) int OnAddPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or); ///< 修改常购商品信息(7295) int OnChangePreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or); ///< 删除常购商品(7296) int OnDeletePreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or); };
在插件类实现文件(wiser.cpp)中增加协议处理函数映射,实现各个协议处理函数.
协议处理函数只列举了7294和7293的对应函数.7295,7296的处理与7294类似,差别是调用CPreferGoods的Update,Delete方法。
MSG_FUNC_MAP CWiser::func_[] = { {7293,MT_REQUEST,(MSGFUNC)&OnQueryPreferGoods,true,"查询常购商品",""}, {7294,MT_REQUEST,(MSGFUNC)&OnAddPreferGoods,true,"新增常购商品",""}, {7295,MT_REQUEST,(MSGFUNC)&OnChangePreferGoods,true,"修改常购商品信息",""}, {7296,MT_REQUEST,(MSGFUNC)&OnDeletePreferGoods,true,"删除常购商品",""}, }; ///< 新增操作常购商品(7294) int CWiser::OnAddPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or) { CMsg *msg = in->msg; long lEId = 0, lUserID = 0; CHECK_MSG_PARAMETER2(msg,"EId",&lEId,-1); ///< 检查并读取消息参数,其它类似宏见BasePlugin.h中的定义 CHECK_MSG_PARAMETER2(msg,"UserID",&lUserID,-1); CRowset *rs = 0; CHECK_MSG_ROWSET(msg,0,rs,-1); ///< 检查消息中的行集 int rec_num = rs->GetRSMeta(RST_ROW_CNT); GETDBC(pdbor,this->local_dbc_.c_str()); FAIL_RETURN(pdbor->BeginTrans(),,-1); for (int i=0;i<rec_num;i++) { CPreferGoods pg; pg.eid_ = lEId; if (pg.LoadFrom(rs,i)) ///< 从消息行集记录加载对象成员 return -1; if (-1==pg.Insert(1)) return -1; } FAIL_RETURN(pdbor->CommitTrans(),,-1); return 0; } ///< 查询常购商品(7293) int CWiser::OnQueryPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or) { CMsg *msg = in->msg; long lEId = 0, lUserID = 0; CHECK_MSG_PARAMETER2(msg,"EId",&lEId,-1); CHECK_MSG_PARAMETER2(msg,"UserID",&lUserID,-1); long co_eid=0; msg->GetParam("CoEId",co_eid); string key_value; msg->GetParam("KeyValue",key_value); bool has_key_value = !key_value.empty(); string cond; ///< 处理查询条件 if (has_key_value) { int key_value_prop = CheckStringProp(key_value); ///< 检查字符串特性:全数字,含汉字 if (key_value_prop&0x01) { ///< 全数字 cond = LogMsg(" (b.barcode like '%%%s%%' or b.mygoodsid like '%%%s%%') ",key_value.c_str(),key_value.c_str()); } else if (key_value_prop&0x02) { ///< 含汉字 cond = LogMsg("b.goodsname like '%%%s%%'",key_value.c_str()); } else { cond = LogMsg(" (b.helpcode like '%%%s%%' or b.mygoodsid like '%%%s%%')",key_value.c_str(),key_value.c_str()); } cond += " and "; } if (co_eid) { cond += LogMsg("a.coeid=%lu",co_eid); cond += " and "; } if (!cond.empty()) cond.erase(cond.length()-5,5); ///< 生成查询SQL string sql = LogMsg("select a.CoEid as EId, a.GoodsID,DataVersion,PicVersion from t_ven_PreferGoods a,t_bas_mygoods b" " where a.eid=%lu and a.coeid=b.eid and a.goodsid=b.goodsid %s %s order by EId,GoodsID",lEId,!cond.empty() ? "and":"",cond.c_str()); return SQLBasedMsgProc(sql,in,out,or); ///执行SQL把结果返回给客户端 }
CPendingOrg po; CMsg *req = new CMsg; req->SetMsgType(MT_REQUEST); req->SetMsgID(1); req->AddParam("MyOrgID","1"); req->AddParam("OrgType",2L); po.LoadFrom(req); ///< CreateTime,Reason字段对应的属性未包含在req消息参数中 ///< 仅设置成员并不能让底层知晓需要修改对应字段,需要显式设置需要修改的字段. CDateTime::GetDateTime(po.create_time_); po.reason_ = 11; po.AddFields(OFFSETOF(CPendingOrg,reason_),OFFSETOF(CPendingOrg,create_time_),-1); ///< 指定需要更新的成员变量,支持一次指定多个成员,以-1作为结束标志 if (po.Update()) return -1;
CORMBase类:
头文件:orm_base.h
实现文件:orm_base.cpp
// class CORMBase { protected: int GenKeyCond(string &cond); ///< 生成条件表达式串 public: virtual string& GetTableName() = 0; virtual vector<CFieldBind*>& GetKeyFields() = 0; public: virtual CRecordsetBindObjectBase* GetBinder() { return 0; } virtual CRecordsetBindObjectBase* NewBinder(const char *fld,...) { return 0; } virtual CRecordsetBindObjectBase* NewBinder(const char *fld,va_list arg) { return 0; } ///< 检查对象数据是否有效 ///< @return -1:错误 0:无效 1:有效 virtual int Check() { return 1; } /// @return 0-成功 -1-失败 1-存在主键冲突 virtual int Insert(short on_dup_error=2); ///< on_dup_error 忽略-1,按错误处理-2,更新-3 virtual int Update(); ///< 修改对象 virtual int Update(const char *fld,...); ///< 修改指定的字段 virtual int Select(const char *fld,...); ///< 查询指定的字段 virtual int Delete(); virtual int Load(); ///< 从数据库加载对象 virtual int LoadFrom(CMsg *msg); ///< 从消息参数加载对象成员 virtual int LoadFrom(CRowset *rs,unsigned long recno); ///< 从消息行集记录加载对象 virtual int SaveTo(CMsg *msg); ///< 把对象成员输出到消息参数 virtual int ToField(CRowset *rs); ///< 把对象成员输出到行集列信息 virtual int SaveTo(CRowset *rs); ///< 把对象成员输出到行集记录(新增记录) virtual int LoadFrom(CRecordset *rs); ///< 从查询记录集加载对象成员 };