iBatis + SQL Server 项目开发实战小结
几年前跟随项目经理做的一个ERP小项目,自己业余时间整理的开发手册,供参考。
开发环境配置:编程环境为Microsoft Visual Studio 2010,数据库是SQL Server 2008 R2。设计架构Windows Forms+ .NET Remoting + SQL Server,所有程序的代码量(框架,工具,业务逻辑)在5万行以内。
%26#160;
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框架要求每个表要以Id作为主键,类型为(C#:Int64, Sql:bigint)是个自增型。因为Id是唯一的,所以从表不需要设计多主键与主表关联。
如果是从表(明细表),则需要添加对主表的Id列的外键引用。参考下面的脚本例子。
ALTER TABLE [dbo].[tbCustomerDiscBankAcct] ADD CONSTRAINT [FK_tbCustomerDiscBankAcct_tbCustomer] FOREIGN KEY ([IdCustomer]) REFERENCES [dbo].[tbCustomer] ([Id])
%26#160;
2%26#160; 设计iBatis映射文件
添加Xml映射文件,放置于ErpMappingClass\Model\SqlMap目录中,同时设置它的生成动作(Build Action)是嵌入式资源(Embedded Resource)
打开FMVendor.xml文件,增加内容如下所示,namespace的值为实体类型名称
%26lt;sqlMap namespace=%26quot;FMVendor%26quot; xmlns=%26quot;http://ibatis.apache.org/mapping%26quot; xmlns:xsi=%26quot;http://www.w3.org/2001/XMLSchema-instance%26quot;%26gt; %26lt;alias%26gt; %26lt;typeAlias alias=%26quot;FMVendor%26quot; type=%26quot;Erp.Model.FMVendor%26quot;/%26gt; %26lt;/alias%26gt;
type的值为实体类型定义的完整名称。
在Xml文件中增加不同的节(Element),用于SQL语句对数据的增删查改。
数据操作语句 |
Xml片段写法 |
读取 |
%26lt;select id=%26quot;Select%26quot; parameterClass=%26quot;Hashtable%26quot; resultClass=%26quot;FMVendor%26quot; %26gt; |
插入新数据 |
%26lt;insert id=%26quot;Insert%26quot; parameterClass=%26quot;FMVendor%26quot; resultClass=%26quot;Erp.Model.BaseClass%26quot; %26gt; |
更新数据 |
%26lt;update id=%26quot;Update%26quot; parameterClass=%26quot;FMVendor%26quot; %26gt; |
删除数据 |
%26lt;delete id=%26quot;Delete%26quot; parameterClass=%26quot;Hashtable%26quot;%26gt; delete $Tablename$ where Id=#Id# %26lt;/delete%26gt; |
注意参数的写法:ColumnName=#ColumnName# 以”#”表示列名对应的参数。
删除数据的Xml代码在BaseClass对应的Xml节中有配置,此处不用写。
%26#160;
3%26#160; 设计实体类型及数据增删查改
实体类型的代码根据Code Smith模板自动生成,同时加上Serializable以支持用于远程调用时对象的可序列化。
3.1%26#160; 根据数据库表的列,生成实体类型定义
[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 %26quot;tbFMVendor%26quot;; }
3.2 读取一个实体对象(三个重载方法,前两个方法传入不同的参数调用第三个方法)
public static FMVendor getFMVendorById(Int64 _Id) { Hashtable htFilter = new Hashtable(); htFilter.Add(%26quot;Id%26quot;, _Id); return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByCode(String _Code) { Hashtable htFilter = new Hashtable(); htFilter.Add(%26quot;Code%26quot;, _Code); return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByFilter(Hashtable _htFilter) { FMVendor fmVendor = Select%26lt;FMVendor%26gt;(_htFilter 3ff7 ); return fmVendor; }
%26#160;3.3 读取一个实体集合(两个重载方法,前一个方法传入空值调用第二个方法)
public static IList%26lt;FMVendor%26gt; getAllFormKind() { return getAllFormKindByFilter(null); } public static IList%26lt;FMVendor%26gt; getAllFormKindByFilter(Hashtable _htFilter) { IList%26lt;FMVendor%26gt; list = Global.QueryForList%26lt;FMVendor%26gt;(%26quot;FMVendor.Select%26quot;, _htFilter); return list; }
%26#160;3.4%26#160; 保存数据 保存前做数据验证,验证信息以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 = %26quot;请输入供应商类型代码! %26quot;; return result; } if (string.IsNullOrEmpty(_fmVendor.Name)) { result.Succeed = false; result.Message = %26quot;请输入供应商类型名称! %26quot;; 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 %26amp;%26amp; formkindExist.Id != _fmVendor.Id) { result.Succeed = false; result.Message = %26quot;供应商代码重复,请重新输入一个新代码!%26quot;; 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%26#160; 删除数据
public static String deleteFMVendor(Int64 vendorId) { DataResult result = new DataResult(); try { FMVendor fmVendor = GsctErp.Model.FMVendor.getFMVendorById(vendorId); if (fmVendor != null) { fmVendor.Delete(); } return %26quot;删除成功 ! %26quot;; } catch (Exception ex) { return %26quot;删除失败 ! %26quot; + ex.Message; } }
FMVendor.xml文件中不需要写SQL删除数据语句,基类型BaseClass中已经有删除方法实现。
%26#160;
4%26#160; 设计远程对象(.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%26lt;NroFMVendor%26gt;(); return nroFMVendor; } } }
修改类型Erp.Model.FlextFactory的RegisterService方法,公开远程服务。
public class FlexFactory { RemotingConfigurationRegisterWellKnownServiceType%26lt;NroFMVendor%26gt;();
5 界面开发
在项目中增加窗体FrmDtlVendor.cs,继承于FrmDtlBase。
public partial class FrmDtlVendor : FrmDtlBase { public FrmDtlVendor() { InitializeComponent(); } private FMVendor _vendor; public void setVendor(FMVendor vendor) { _vendor = vendor; }
它是对单笔数据进行操作,界面如下所示
ERP系统中预定义的窗体基类型列表如下,可根据业务需要继承。
类型名称 |
用途 |
FrmLstBase |
以列表形式呈现数据 |
FrmDtlBase |
单笔数据的编辑(增删查改)操作 |
FrmSchClass |
数据搜索窗体 |
FrmRptFlt |
报表参数值选择 |
FrmScnClass |
查询方案 |
FrmImpBase |
数据导入 |
回到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(%26quot;保存成功!%26quot;); } else { MessageBox.Show(%26quot;保存失败!\r\n%26quot; + 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; }
%26#160;
项目总结
1%26#160; iBatis是轻量型ORM,可以将SQL语句返回的结果绑定到对象实体,不过手写SQL语句和增加实体定义文件这两步需要开发人员自己完成。所以需要另外开发Code Smith模板生成代码。
2%26#160; 项目很小,仅限于公司内部员工使用,欠缺很多商业性ERP的特性,欢迎批评指正。
- iBatis + SQL Server 项目开发实战小结
- 项目开发-iBatis事务源码之SQL执行
- 【服务计算】| 简单 web 服务与客户端开发实战---项目小结
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(3)
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)
- dwr+ibatis+sql server 2000开发的小程序
- 《Microsoft Office SharePoint Server 2007案例实战开发》之对象介绍
- 开发项目细节问题小结
- Swift项目开发实战-基于分层架构的多版本iPhone计算器-直播公开课
- 2019新版《Java开发淘宝高性能购物车项目实战课程完整版》
- 安卓项目开发实战(2)--星座展示12宫格
- 2017.7.1 慕课网-Java从零打造企业级电商项目实战:3 category模块设计与开发
- J2EE金融项目开发实战视频教程
- 金融项目开发之sql安全
- iOS项目开发实战——自定义圆形进度提示控件
- FreeRTOS开发实战_FreeRTOS内核配置项目解析
- asp.net4.0网站开发与项目实战—学习笔记1
- ASP.NET架构设计&.NET 分布式架构开发项目实战收集
- OA项目实战(二) 开发准备
- iOS项目开发实战——使用用户首选项数据进行启动提示