首页 > 代码库 > [原创]如何写好SqlHelper 之终章
[原创]如何写好SqlHelper 之终章
精简的美丽......
标题有点大。但是,我觉得99%的接近了。
好了,下面我们来说说一个SqlHelper为了适应各种不同的业务需要,它应该具备哪些基本要素。
第一点、可控的事务。
事务是数据库操作的关键部分,在对数据库进行插入、修改、删除时都会用到;事务是基于数据库连接的。一个事务必须要运行在一个连接上,但一个连接上可以有多个事务。
可控的事务就是你在进行数据的增删改时需要一个事务来进行辅助,当操作出错时将修改的数据还原。类似下面的过程
transaction.begin(); ...do something...transaction.commit();or errortransaction.rollback();
当然,你会说,我有部分代码不需要事务。对!向操作日志之类的东西,只要你的语句没错,不管其它的操作成功与否都需要写入数据库。这样的操作就可以护理事务了。
可控的事务其它隐匿包含两种操作。一种是普通的单表增删改操作。一种是多表增删改操作,多见于主从表。
那么,我们需要使用两套(标准或)代码来对以上两种情况进行处理嘛?
第二点,多样的输入方式。
何为输入方式?哈哈,这个很简单嘛!CommandText 与 Parameters。数据库管理系统发展到今天早已经超出使用sql命令的范围,在NoSql数据库越来越成熟的情况下,关系型数据库却依然是主流。所以,哈哈。我们还是谈CommandText 与 Parameters。
向数据库发现Sql命令的方法只有通过文本的形式,然后Parameters的使用却可以为编程以及数据库安全提供帮助。例如:
var sql = new StringBuilder(); sql.Append(" INSERT INTO xACCOUNT"); sql.Append(" (USERID,"); sql.Append(" ACCOUNT,"); sql.Append(" EMAIL,"); sql.Append(" STATE,"); sql.Append(" PWD,"); sql.Append(" CREATEOPTION,"); sql.Append(" PROPERTY,"); sql.Append(" JOINDATE,"); sql.Append(" ATTENMODE)"); sql.Append(" VALUES"); sql.Append(" (:pUSERID,"); sql.Append(" :pACCOUNT,"); sql.Append(" :pEMAIL,"); sql.Append(" :pSTATE,"); sql.Append(" :pPWD,"); sql.Append(" :pCREATEOPTION,"); sql.Append(" :pPROPERTY,"); sql.Append(" :pJOINDATE,"); sql.Append(" :pATTENMODE)"); return sql.ToString();
如果你需要向数据库中连接插入1W条数据,我想你第一想到的应该是使用带参数的insert语句。为什么?直白点说,数据库对这条语句做出了优化。如果你需要向数据库插入10w以上的数据,那么就不要用这个方法了。有个以datatable方式插入的方法,秒级。
数据安全方面,这个最普通的说法是防止恶意sql语句攻击。百度一下应该大把资料。
第三点,高复杂度与低耦合。
什么是高复杂度。即:所有对数据库的操作高度封装。再解释:.net提供了数据操作的接口对象,如Connectin,Command,Transaction,DataReader,DataAdapter,DataSet,DataTable,Parameter等等对象。这些几乎是数据库操作时必不可少的元素。然后这些对象对于我们的开发来说还是过于分散,因为我们更多的是关心操作的结果。所以,将它们再次封装后,只留下,DataSet,DataTable,DataReader,Execute(),ExecuteScalar()就足够我们使用的了。
什么是低耦合。即:业务类只做业务逻辑相关的事,将数据处理交给数据处理层去做(纯废话,大家都知道的)。这个要怎么实现?
下面,我来展示两个图一个文件列表。通过这些来说明上述三点是如何实现的。
图一,类模型
从这个图,你能看到各对象之间的关联。按照.net一个对象一个文件的作法。这也表示,我的文件列中有6个文件。 |
图二,类结构图。哈哈,这是你最次要想知道的。你最想要知道的是我的代码是什么?有没有可以下载的地方?
最后是文件列表,
下面我将正向的介绍这6个文件(类)
1、ErrorObject 。业务基类(blo)的基类。用于描述错误信息。它很简单只有两个属性一个方法。
属性: IsSucceed,用于判断一个(系列)业务操作是否成功。
属性:ErrorCode,表示出错的代码。这里只存储出错代码。具体出错信息需要另行存储,以方便管理。
2、Blo 。业务类的基类。所有的业务类需要从这里继承。为什么?还记得“可控事务”最后的问题嘛!这个就是答案。
2.1 独立业务操作
设:
public class TestA : Blo{ public bool Add(object obj) { Transaction.Begin(); try { ............ Transaction.Commit(); } catch (Exception ex) { Transaction.RollBack(); ErrorCode = -1111; throw; } }}
| 这里业务类TestA的操作在自己的事务过程中 |
2.2 大事务业务操作。
public class TestB : Blo{ public bool Add(object obj) { Transaction.Begin(); try {
| 这里业务类TestB的操作在自己的事务过程中。而TestA的操作则在TestB的事务过程里。对,没错,它们共享了一个事务过程。 |
共享事务过程的办法就是 Transaction 要做的事了。这个过会再讲。
3、Dao 数据访问层。说白了就是写sql语句的地方。
在这里你可以直接写sql语句,也可以使用参数的形式。不管使用哪种方法,最终的目的是通过制造sql命令,使用OracleHelper对数据库进行操作,并将得到的结果返回给blo层。
上一个直接sql的代码片段
using (var helper = new OracleHelper { ConnectionString = txtOracle.Text, CommandText = txtSql.Text }) { var dt = helper.GetDataTable(); if (dt != null && dt.Rows.Count > 0) { foreach (DataRow row in dt.Rows) { lbResult.Items.Add(row.Field<string>("TABLE_NAME") + "\t" + row.Field<string>("COMMENTS")); } } }
4、ParameterList。参数列表对象。
这个对象,在我的上一版本是没有的。之所以现在增加,是因为我在最近写一个导入工具时发现,我原来写的那个helper在对参数进行操作时只能一次性操作。即AddParameter方法只会在调用时才能赋值,而不能在第二次以上的循环中对其赋值。且不能保存对参数列表的引用。
对象中加入了属性操作
public OracleParameter this[string parameterName]
以通过参数名称来快速检索参数对象。内部存储使用的是Dictionary对象。
同时对象增加了CopyTo方法,可以将参数对象全部导出。
5、Transaction 业务逻辑中的事务控制器。
此对象为事务共享时控制是否共用同一事务的关键。主要的方法为
public void AddBusiness(Blo business)
将多个需要共享事务的业务对象放到同一事务中。属性
public bool IsRelated { get;private set; }
用于判断是否为共享事务。这个属性为只读的。当业务类进行事务操作时,判断IsRelated是否为true。若为True则跳过事务处理(其实它没有自己的事务处理过程,所以也就没办法提交,只能跳过)。
6、OracleHelper 数据库处理类。
本文的重点,也是大家最熟悉的。每个人的处理方式不同。但接口只要差不多就行了。略过。
最后,你最关心的问题,代码在哪里,哪里可以下载!哈哈。。。哈哈。。。
我在这只能说抱歉了。授人以鱼,不如授人以渔。在我这个文的基本上,你动动手,活动活动脑子,我想也能搞个八 九 不 离 十 吧。
别太懒了。
[原创]如何写好SqlHelper 之终章