最近一段时间在做C/S部分的回顾和总结工作,这些天不断的敲Demo,看博客,对之前的《个人机房重构》有了一个更深入的认识,对之前运用的不熟的知识再次回顾,加深自己的理解。
通过一个关于《机房》的综合实例的练习,再次回顾关于泛型、存储过程+事务、视图和触发器的应用。
知识点一、泛型:
泛型简单的说是一种语言特性,这种语言的特性在强类型语言的编程中尽量保证类型安全和类型转换次数。在三层的设计思想中,对于D层查询参数的传递返回结果等有时候是DataTable类型的数据,这样,我们在其他层上想要使用这些数据的时候就要知晓返回表中的字段,用面向对象的思想编程,用这种方式是不可取的,层与层之间的连接多在与接口打交道,不用每一层都要知晓数据库的结构,因为我们传递的数据多数都是以实体的方式进行,所以所有的数据传递均以实体为根本,为了避免上述的表操作,我们需要在D层对于返回表的结果转化为实体,然后返回到其它层里,那么如何转换呢?此时泛型就发挥作用了。
如何使用泛型呢?首先将DataTable类型的数据全部转化为实体,将实体提取出来放到一个泛型集合中,返回这个集合,下面以一个Demo为例讲解如何使用泛型:
1、既然要对表进行实体的转换,必然有一个实体转换的类来完成这个转换
Public Shared Function convertEntityToList(Of T As {New})(ByVal table As DataTable) As IList(Of T) '将datatable转化为泛型集合
'传入的参数是一个类,将表传入然后进行转换
Dim myList As New List(Of T) '定义最终返回的集合
Dim myTpye As Type = GetType(T) '得到实体类的类型名
Dim dr As DataRow '定义行集
Dim tempName As String = String.Empty '定义一个临时变量
'遍历DataTable的所有数据行
For Each dr In table.Rows
Dim myT As New T '定义一个实体类的对象
Dim propertys() As PropertyInfo = myT.GetType().GetProperties() '定义属性集合
Dim Pr As PropertyInfo
'遍历该对象的所有属性
For Each Pr In propertys
tempName = Pr.Name '将属性名称赋值给临时变量
'检查DataTable是否包含此列(列名==对象的属性名)
If (table.Columns.Contains(tempName)) Then '将此属性与datatable里的列明比较,查看datatable是否包含此属性
'判断此属性是否有Setter
If (Pr.CanWrite = False) Then '判断此属性是否可写,如果不可写,跳出本次循环
Continue For
End If
Dim value As Object = dr(tempName) '定义一个对象型的变量来保存列的值
If (value.ToString <> DBNull.Value.ToString()) Then '如果非空,则赋给对象的属性
Pr.SetValue(myT, value, Nothing) '在运行期间,通过反射,动态的访问一个对象的属性
End If
End If
Next
myList.Add(myT) '添加到集合
Next
Return myList '返回实体集合
End Function
2、在D层中我们每次将用table接收的数据,利用convertEntityToList函数进行转换即可,它的含义就是遍历表中的各个属性,将表中的每一行记录作为一个对象
放到List中,这样,表中的字段就是List表中的对象属性。(注意List中的属性和数据库表中的字段要一致,否则转换失败,出现找不到集合中的对象)。
Dim mylist As New List(Of Entity.StudentCardEntity)
Dim table As DataTable
table = SqlHelper.SqlHelper.GetDataTable(sql, CommandType.Text, paras)
mylist = SqlHelper.SqlHelper.convertEntityToList(Of Entity.StudentCardEntity)(table)
Return mylist
所谓表中的字段就是List表中的对象的属性,这里主要体现在实体层代码中的编写,我们要让每一个属性名字和数据库表中的字段相对应
(这里的userID是和数据库中T_UserInfo表中的字段同名的)
在三层之间加入泛型的使用就很好的解决了不能返回实体的问题。
知识点二、存储过程+事务
对于之前的博客讲解了什么是存储过程,《机房收费系统》中关于组合查询功能的实现用的就是存储过程,这次我们主要讲解一下事务
事务就是一个单元中数据库从一个稳定状态到另一个稳定状态的一系列操作,这些操作的执行要么全部成功,要么全部失败。
四个属性:
1、原子性 :要么都执行,要么都不执行,不可分割。
2、一致性:事务完成后,必须使所有的数据保持一致的状态。维护数据库数据的完整性!
3、隔离性:查看一个事务返回的数据的状态时要么是已经修改完成提交的状态,要么是事务修改之前的状态,对于正在修改但没有提交的数据,事务的隔离性保证了不与查看。
4、持久性:事务完成之后,它对于系统的影响是永久性的。
下面以一个注册学生的功能为例(卡表和学生表是分开的),浅谈一下存储过程+事务是如何应用的。
CREATE PROCEDURE [dbo].[PRO_Register]
@cardID varchar(20),
@cardType varchar(20),
@studentID varchar(20),
@studentName varchar(20),
@sex varchar(20),
@grade varchar(20),
@studentClass varchar(20)
AS
begin
set nocount on
begin Tran
insert into T_CardInfo (cardID ,cardType ,studentID ) values(@cardID ,@cardType ,@studentID )
insert into T_StudentInfo (studentID ,studentName ,sex ,grade ,studentClass )values(@studentID ,@studentName ,@sex ,@grade ,@studentClass )
IF @@ERROR <>0 --判断错误计数器中的值是否为0
ROLLBACK Tran --出错,事务回滚,恢复到初始状态,没有任何改动
ELSE
COMMIT Tran --提交事务
end
GO
在上面的事务编写中如果错误行数为0(@@ERROR=0),表示没有发生错误,即可提交事务,另外还有其他的参数可以表示事务可以提交,这里不详细介绍了。
下面是对D层的编写,相比于其他D层的区别就是这个时候的sql语句是调用创建好的存储过程:
另外一个不同的地方就是调用SqlHelper中的带参非查询操作函数的时候传入的参数不在是CommandType.text,而是CommandType.StoreProcedure。
知识点三、视图
当在进行尤其是多表的查询操作时,视图就是我们最先考虑的东西,他可以将多个表联合起来,构建一个视图,把需要查询的信息放到视图中,对于多表查询
操作相当的容易。下面就以查询学生信息为实例讲解应用视图:
如图所示创建一个视图:
其中studentID是T_CardInfo表的外键,是T_StudentInfo表的主键,视图中两个表就是通过外键相连的。
同样在D层中和其他的不同就是sql语句的区别,把查询表换成了查询视图,
对于多表的查询操作,应用视图非常方便,省略了好多重复的查询操作,对于如何操作视图,这里不再介绍。
上面就是对整个《机房》中应用的知识(设计模式除外)的整体回顾,对于《个人重构》已经完成好长时间了,这么长的时间没有敲代码,感觉生疏了不少,希望在开始一个新的部分的学习之前,对之前的知识有一个整体的把握,这段时间通过阅读大家的博客,自己动手实践例子,收获很多,对知识结网,颗粒归仓,感觉很好,知识贵在反复!