转载

iBatis + SQL Server 项目开发实战小结

几年前跟随项目经理做的一个ERP小项目,自己业余时间整理的开发手册,供参考。

开发环境配置:编程环境为Microsoft Visual Studio 2010,数据库是SQL Server 2008 R2。设计架构Windows Forms+ .NET Remoting + SQL Server,所有程序的代码量(框架,工具,业务逻辑)在5万行以内。

1 SQL Server 数据库表设计

设计供应商表Vendor, tb是通用前缀标识符号。FM是资金管理Finance Management。

CREATE TABLE [dbo].[tbFMVendor]( [Id] [bigint] IDENTITY(1,1) NOT NULL, [Code] [nvarchar](50) NULL, [Name] [nvarchar](50) NULL, [Description] [nvarchar](50) NULL, CONSTRAINT [PK_tbVendor] PRIMARY KEY CLUSTERED  ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  ) ON [PRIMARY]

iBatis + SQL Server 项目开发实战小结

iBatis框架要求每个表要以Id作为主键,类型为(C#:Int64, Sql:bigint)是个自增型。因为Id是唯一的,所以从表不需要设计多主键与主表关联。

如果是从表(明细表),则需要添加对主表的Id列的外键引用。参考下面的脚本例子。

ALTER TABLE [dbo].[tbCustomerDiscBankAcct] ADD CONSTRAINT [FK_tbCustomerDiscBankAcct_tbCustomer] FOREIGN KEY ([IdCustomer]) REFERENCES [dbo].[tbCustomer] ([Id])

2  设计iBatis映射文件

添加Xml映射文件,放置于ErpMappingClass/Model/SqlMap目录中,同时设置它的生成动作(Build Action)是嵌入式资源(Embedded Resource)

打开FMVendor.xml文件,增加内容如下所示,namespace的值为实体类型名称

<sqlMap namespace="FMVendor" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <alias> <typeAlias alias="FMVendor" type="Erp.Model.FMVendor"/> </alias>

type的值为实体类型定义的完整名称。

在Xml文件中增加不同的节(Element),用于SQL语句对数据的增删查改。

数据操作语句

Xml片段写法

读取

<select id="Select" parameterClass="Hashtable" resultClass="FMVendor" >

插入新数据

<insert id="Insert" parameterClass="FMVendor" resultClass="Erp.Model.BaseClass" >

更新数据

<update id="Update" parameterClass="FMVendor" >

删除数据

<delete id="Delete" parameterClass="Hashtable">

delete $Tablename$ where Id=#Id#

</delete>

注意参数的写法:ColumnName=#ColumnName# 以”#”表示列名对应的参数。

删除数据的Xml代码在BaseClass对应的Xml节中有配置,此处不用写。

3  设计实体类型及数据增删查改

实体类型的代码根据Code Smith模板自动生成,同时加上Serializable以支持用于远程调用时对象的可序列化。

3.1  根据数据库表的列,生成实体类型定义

[Serializable] public class FMVendor: BaseClass {  public string Code { get; set; } public string Name { get; set; } public string Description { get; set; }  }

重写BaseClass的tm_getTableName方法,返回实体映射的数据库表名称。

protected override string tm_getTableName() {       return "tbFMVendor"; }

3.2 读取一个实体对象(三个重载方法,前两个方法传入不同的参数调用第三个方法)

public static FMVendor getFMVendorById(Int64 _Id) {      Hashtable htFilter = new Hashtable();      htFilter.Add("Id", _Id);      return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByCode(String _Code) {  Hashtable htFilter = new Hashtable();  htFilter.Add("Code", _Code);  return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByFilter(Hashtable _htFilter) {   FMVendor fmVendor = Select<FMVendor>(_htFilter);   return fmVendor; } 

3.3 读取一个实体集合(两个重载方法,前一个方法传入空值调用第二个方法)

public static IList<FMVendor> getAllFormKind() {  return getAllFormKindByFilter(null); } public static IList<FMVendor> getAllFormKindByFilter(Hashtable _htFilter) {    IList<FMVendor> list = Global.QueryForList<FMVendor>("FMVendor.Select", _htFilter);    return list; } 

3.4  保存数据 保存前做数据验证,验证信息以DataResult对象传回

public static DataResult saveFMVendor(ref FMVendor _fmVendor, String _userCode) {   DataResult result = new DataResult(false);   if (string.IsNullOrEmpty(_fmVendor.Code))  {     result.Succeed = false;     result.Message = "请输入供应商类型代码! ";     return result;   }   if (string.IsNullOrEmpty(_fmVendor.Name))   {      result.Succeed = false;      result.Message = "请输入供应商类型名称! ";      return result;   } //数据校效 User user = User.getUserByCode(_userCode); FMVendor fmVendor = getFMVendorById(_fmVendor.Id); if (fmVendor == null) {   _fmVendor.Id = 0; } FMVendor formkindExist = getFMVendorByCode(_fmVendor.Code); if (formkindExist != null && formkindExist.Id != _fmVendor.Id) {     result.Succeed = false;    result.Message = "供应商代码重复,请重新输入一个新代码!";    return result; } try {  Global.BeginTransaction();  _fmVendor.Save();  Global.CommitTransaction();  result.Succeed = true;  return result; } catch (Exception ex) {  Global.RollBackTransaction();  result.Succeed = false;  result.Message = ex.Message;  return result; } } 

3.5  删除数据

public static String deleteFMVendor(Int64 vendorId) {    DataResult result = new DataResult();    try    {         FMVendor fmVendor = GsctErp.Model.FMVendor.getFMVendorById(vendorId);         if (fmVendor != null)         {          fmVendor.Delete();          }          return "删除成功 ! ";     }     catch (Exception ex)     {        return "删除失败 ! " + ex.Message;     } } 

FMVendor.xml文件中不需要写SQL删除数据语句,基类型BaseClass中已经有删除方法实现。

4  设计远程对象(.NET Remoting)

远程对象需要公开相应的数据访问方法给客户端界面调用。

public class NroFMVendor: BaseClass {  }

远程对象命名规则在原有的对象名称前加Nro前缀,并继承于BaseClass,它的方法是对实体对象的数据访问的封装,每个方法的第一行是用户验证代码。

public FMVendor getFMVendorById(Int64 _Id) {     User user = ValidateIdentity();     return FMVendor.getFMVendorById(_Id); } public FMVendor getFMVendorByCode(string Code) {  User user = ValidateIdentity();  return FMVendor.getFMVendorByCode(Code); } public DataResult saveFMVendor(ref FMVendor vendor, String _userCode) {  User user = ValidateIdentity();  return FMVendor.saveFMVendor(ref vendor, _userCode); } 

修改类型Erp.Model. FlexFactory,在该类型中增加私有静态变量

增加公共属性,以用于客户端的界面访问,需要增加的代码如下所示

public class FlexFactory {  private static NroFMVendor nroFMVendor;  public static NroFMVendor myNroFMVendor  {     get {      if (nroFMVendor == null)        nroFMVendor = ActivatorGetObject<NroFMVendor>();     return nroFMVendor;     }   } } 

修改类型Erp.Model.FlextFactory的RegisterService方法,公开远程服务。

public class FlexFactory {      RemotingConfigurationRegisterWellKnownServiceType<NroFMVendor>();

5 界面开发

在项目中增加窗体FrmDtlVendor.cs,继承于FrmDtlBase。

public partial class FrmDtlVendor : FrmDtlBase {  public FrmDtlVendor() {  InitializeComponent(); } private FMVendor _vendor; public void setVendor(FMVendor vendor) {  _vendor = vendor; } 

它是对单笔数据进行操作,界面如下所示

iBatis + SQL Server 项目开发实战小结

ERP系统中预定义的窗体基类型列表如下,可根据业务需要继承。

类型名称

用途

FrmLstBase

以列表形式呈现数据

iBatis + SQL Server 项目开发实战小结

FrmDtlBase

单笔数据的编辑(增删查改)操作

iBatis + SQL Server 项目开发实战小结

FrmSchClass

数据搜索窗体

iBatis + SQL Server 项目开发实战小结

FrmRptFlt

报表参数值选择

iBatis + SQL Server 项目开发实战小结

FrmScnClass

查询方案

iBatis + SQL Server 项目开发实战小结

FrmImpBase

数据导入

iBatis + SQL Server 项目开发实战小结

回到FmVendorDtl窗体中,在OnLoad方法加载数据。

protected override void OnLoad(EventArgs e) {  this.Text = SysParam.ClientSysTitle;  setRight();  FMVendor fmVendor = _vendor == null ? null : GsctFactory.myNroFMVendor.getFMVendorById(_vendor.Id);  if (fmVendor == null)   clearForm();   else    showVendor(fmVendor);  bindProfile();  this.CenterToParent(); } 

有二种方法启动这个窗体,当从List列表进入时,根据传入的对象值加载数据,并绑定到界面控件中,同时设置权限,对控件进行隐藏或是禁用处理。

增加数据保存代码,示例方法如下所示

private void tsbSave_Click(object sender, EventArgs e) {  DataResult result = saveVendor();  if (result.Succeed)  {    showVendor(_vendor);    MessageBox.Show("保存成功!");  }  else  {   MessageBox.Show("保存失败!/r/n" + result.Message);   } } 

saveVendor方法中的对象保存代码是调用远程对象的方法,片段如下

try {      vendor.Code = txtCode.Text.Trim();     vendor.Name = txtName.Text;     result = GsctFactory.myNroFMVendor.saveFMVendor(ref vendor, User.currUser.Code);     if (!result.Succeed)
throw new Exception(result.Message); } catch (Exception ex) {      result.Message = ex.Message;      return result; }

项目总结

1  iBatis是轻量型ORM,可以将SQL语句返回的结果绑定到对象实体,不过手写SQL语句和增加实体定义文件这两步需要开发人员自己完成。所以需要另外开发Code Smith模板生成代码。

2  项目很小,仅限于公司内部员工使用,欠缺很多商业性ERP的特性,欢迎批评指正。

正文到此结束
Loading...