对一个数据库表进行增,删,修改,查询操作是常见的编程任务.
统一和简化此类开发任务,可以提高开发效率,包括实现,调试和维护.
本文以常购商品的功能为例,说明基于需求完整实现的过程.
常购商品的4协议如下:
.新增常购商品(7293)
支持批量新增
.修改常购商品(7294)
支持批量修改
.删除常购场频(7295)
支持批量删除
.查询常购商品(7296)
常购商品数据维护的协议都设计为支持批量操作.
并非所有操作都要支持批量,取决于交互操作界面规定的方式,批量处理一般对应一个列表界面,记录行有一个勾选框。
常见的批量操作有删除,状态改变。
新增和修改并不总是要求批量操作。
.协议在哪个插件中实现:本例为wiser插件
.新增哪些类,类的头文件,实现文件(cpp):如果可被其它插件访问则把文件放到common目录下.
*协议设计时要求协议的参数和行集列名称与数据库表一致.
CPreferGoods类定义:在wiser_data.h
插件工程中加入common\orm_base.cpp.
增加CPreferGoods绑定(wiser_data.cpp)
协议处理函数只列举了7294和7293的对应函数.7295,7296的处理与7294类似,差别是调用CPreferGoods的Update,Delete方法。
实现文件:orm_base.cpp
统一和简化此类开发任务,可以提高开发效率,包括实现,调试和维护.
本文以常购商品的功能为例,说明基于需求完整实现的过程.
1.功能与协议
提供门店经常采购商品信息的管理,提供对快捷订单的支持.
一个协议的功能单一化,避免让一个协议承载太多功能,协议与用例一一对应作为设计原则。常购商品的4协议如下:
.新增常购商品(7293)
支持批量新增
.修改常购商品(7294)
支持批量修改
.删除常购场频(7295)
支持批量删除
.查询常购商品(7296)
常购商品数据维护的协议都设计为支持批量操作.
并非所有操作都要支持批量,取决于交互操作界面规定的方式,批量处理一般对应一个列表界面,记录行有一个勾选框。
常见的批量操作有删除,状态改变。
新增和修改并不总是要求批量操作。
2.实现
以下内容在设计阶段规定:
.相关表结构:本例有t_Ven_PreferGoods.协议在哪个插件中实现:本例为wiser插件
.新增哪些类,类的头文件,实现文件(cpp):如果可被其它插件访问则把文件放到common目录下.
*协议设计时要求协议的参数和行集列名称与数据库表一致.
2.1根据表结构定义类.
常购商品表(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();
}
2.2协议实现
在插件类中(wiser.h文件中)增加协议处理函数定义.class CWiser
: public CQQBasePlugin {
///< 查询常购商品(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把结果返回给客户端
}
3.特殊处理
(1)从消息参数或行集获取对象属性后,需要额外设置其它未包含在消息包中的属性时:
使用:int AddFields(unsigned short offset,...)
示例:
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;
4.相关代码说明
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); ///< 从查询记录集加载对象成员
};