SmartPersistenceLayer 2.0之实体操作篇
通过上一篇的”设置篇”后,在我们的系统中就可以开始使用SPL的强大功能了,这一节我主要是介绍SPL的实体(Entity)操作。
原理
SPL中的实体可能与其他PL的实体有所区别,一般如果从理论上讲的话,实体仅仅是数据的属性描述,不应该带有操作方法,这种方式也存在一种缺点,就是实体没有了操作,只能借助”辅助类”进行实体的操作,而这种操作势必不能是强类型的,而且开发员可能无法彻底站在面向对象的角度开发系统,因此,SPL中采用了实体中集成部分常用操作,即Insert,Update,Delete,Retrieve操作,这样当我们要对实体进行操作时,可以直接调用实体的这些方法,实现强类型的操作控制。
为了让SPL能自动实现这种功能,实体(Entity)要继承SPL中的EntityObject抽象类,如:
Public class StudentEntity : EntityObject
这样,这个实体就自动具有了Save(),Retreve(),Delete()的功能。
实体操作
实体的常用操作为新增(Insert)、更新(Update)、删除(Delete) 、获取(Retrieve),在SPL中体现为Save(),Delete(),Retrieve()这三个方法,Save()会自动判断实体是否已经持久化(IsPersistence)来进行Insert和Update区分。
下面我还是以上一篇”基础设置篇”中的例子来演示:
实体的XML定义如下:
<class name="StudentEntity" table="Student" database="MyDB">
<attribute name="Id" column="Id" type="Integer" increment="true" key="primary" />
<attribute name="No" column="No" type="String" key="primary" />
<attribute name="Name" column= StudentEntity "Name" type="String" />
<attribute name="Birthday" column="Birthday" type="Date" />
<attribute name="Grade" column="Grade" type="Integer" />
<attribute name="Score" column="Score" type="Decimal" />
</class>
实体类如下:内容略,请参照”基础设置篇”
namespace BusinessEntity
{
using System;
using System.Collections;
using System.Data;
using PersistenceLayer;
public class StudentEntity : EntityObject
{
//常量定义部分…
//局部变量定义部分…
//属性定义部分…
}
}
实体新增:
以往的编程模式中,要往数据库的某表中插入一条记录,有人采用SQL语句执行,这要注意SQL语句的正确性,单引号问题(要使用string.replace(“’”,”’’”)进行替换);还有可以采用Command的Parameter的方式,这样就可以避免单引号问题;还有人为了提高性能会采用存储过程,那么这些过程都会影响开发的速度,在SPL中我们的过程变的相当简单了:
StudentEntity student=new StudentEntity(); //实体化一个学生对象
//以下进行属性赋值
//student.Id=1; //此为自动增长,SPL会自动获取
student.No=” 200401 ” ;
student.Name=”张三”;
student.Birthday=DateTime.Parse(“1979-01 -22 ” );
student.Grade=2;
student.Score=580;
try
{ student.Save(); //实体保存
}catch(PlException exp) //抛出异常
{
if(exp.ErrorType==ErrorTypes.NotUnique) //如果是主键冲突
{
Response.Write(“主键冲突”);
}else
throw exp;
}
步骤分析:
1. 直接New一个实体对象,给对象属性赋值
2. 然后Save(),如果发生主键冲突,则可以通过catch PersistenLayer.PlException异常来处理,判断是否为PersistenceLayer.ErrorTypes.NotUnique,这样就可以完成新增记录。
自动增长:如上面的Id是自动增长列,所以在赋值时不需要赋值,在进行Save()后,自动生成的主键值会自动赋给实体Student的Id属性,这样马上可以使用此生成值。
实体获取:
数据插入后,我们会进行更新,删除等操作,当然,在进行这些操作前,我们肯定会很找到这条记录,那先介绍获取Retrieve()。
在我的设计理念中,每个表都会设置一个主键,这会方便查询,SPL中也实出主键功能,实体的Retrieve()就是根据实体的主键,去获取唯一的一条记录,结果要么是一条,要是就是没有。举例 我们要获取学生ID=5的记录:
StudentEntity student=new StudentEntity(); //实例化
student.Id=5; //给主键赋值
student.Retrieve(); //进行Retrieve()操作
if(student.IsPersistence) //如果获取到
{
//给界面上的控件进行赋值
txtName.Text=student.Name;
……
}
步骤分析:
1. 给主键赋值,例子中是单主键,当然我是一直提供单主键的,不过,SPL也是支持多主键的,只要在XML文件中定义为primary=true的例,那么在进行Retrieve()之前就要进行先赋值。
2. 进行Retrieve()操作,这个操作就是从XML文件中读取为主键的字段值,然后从数据库中读取,如果存在,则把值都赋给实体属性;如果不存在,则不进行操作
3. 对实体进行IsPersistence判断,如果获取到,此值为true,如果未获取到为false,因此可以根据此值进行不同的处理。
因此在获取后要使用值时,一定要进行IsPersistence的判断,这是比较好的编程习惯哦。
实体更新:
当我们获取实体后并赋值给界面上的控件后,在客户进行修改后就要进行更新操作。
StudentEntity student=new StudentEntity(); //实例化
student.Id=5; //赋主键值
student.Retrieve(); //获取实体
if(student.IsPersistence) //如果存在
{
student.Name=txtName.Text; //获取新值
…….
try
{
student.Save() //进行更新
}cartch(PlException exp) //捕捉异常
{
if(exp.ErrorType==ErrorTypes.RestrictError) //如果是级联更新异常
{
Response.Write(“有级联更新约束错误”);Response.End(); //友好提示
}else
{
throw exp;
}
}
}
步骤分析:
1. 先根据主键值获取实体,我是非常建议这样做的,因为系统是多人进行操作的,在每次更新前先从数据库获取一下,这样可以避免实体被删除的并发性错误,当然如果要彻底避免这种并发性,我们可以设定时间戳字段,在获取后进行时间戳比较即可,关于时间戳在SPL中的实现我将在以后的文章中讲述。
2. 使用Save()方法进行更新,这与新增时使用同一个方法,这是因为Save()的方法会根据实体的IsPersistence值进行不同的操作:如直接New一个实体,进行Save(),默认是新增的,而通过Retrieve()后,此值为true的情况下,会自动以Update操作。这种机制非常好用,因为有时候,我们可以让SPL自己决定操作。我们也可以在更新前不进行Retrieve(),而直接把IsPersistence值赋为true,也可以进行更新操作。
3. 级联异常处理,在进行更新时我们经常会遇到级联约束异常,你可以捕捉到ErrorTypes.RestrictError进行友好信息提示。
实体删除:
删除操作跟前面的类似,也是非常简单的。
StudentEntity student=new StudentEntity();
student.Id=5;
student.Retrieve();
if(student.IsPersistence)
{
try
{
student.Delete();
}catch(PlException exp)
{
if(exp.ErrorType==ErrorTypes.RestrictError) //如果是级联删除异常
{
Response.Write(“有级联删除约束错误”);Response.End(); //友好提示
}else
{
throw exp;
}
}
}
步骤分析:
1. 跟实体更新一样,先要通过主键,获取实体,在进行IsPersistence的判断后再进行删除,这可以删除并发性错误。
2. 在删除时我们可以捕捉级联删除错误异常,进行友好信息的提示。
扩展功能
动态赋值
有时候我们会遇到这种情况:实体的字段比较多,有些字段的名称是可以循环的定义的,如12个的金额,可以定义为“Price1,Price2…”,象这种为了在赋值与取值时减少输入量,我们通常会采用动态组装属性名,SPL提供了SetAttributeValue(“属性名”,”属性值”)方法来支持这种赋值方式,如:
StudentEntity student=new StudentEntity();
for(int i=1;i<=12;i++)
{
student. SetAttributeValue(“Price”+i.ToString(),i.ToString());
}
当然,要注意的就是,不要出现不存在的属性名。
多帐套功能
在大型系统中我们会遇到多帐套情况,也就是会存在多个帐套数据库,数据结构完全一致,实体在进行操作时(Save,Delete,Retrieve)时,会动态的从不同数据源中获取,为此实体扩展了多帐套功能。
实体在默认情况是读取XML中配置的数据源的,在操作时我们可以动态指定数据源即可,如:
StudentEntity student=new StudentEntity();
student.DatabaseName=”DB 2” ; //动态指定其他的数据源名
student.Id=5;
………….
student.Save();
通过指定DatabaseName的值,来动态指定实体要对具体哪个数据源操作。
这里的DB2是个逻辑数据源,关于多帐套的功能介绍将在另一个文章详细描述。
内存存储
内丰存储是SPL2.0的新功能,如果要让实体存到内存,减少访问数据库次数,从而提高系统的整体性能,只要在实体的XML配置文件中指明IsSaveToMemory=true即可。
如:
<class name="StudentEntity" table="Student" database="MyDB" IsSaveToMemory="true">
<attribute name="Id" column="Id" type="Integer" increment="true" key="primary" />
<attribute name="No" column="No" type="String" key="primary" />
<attribute name="Name" column= StudentEntity "Name" type="String" />
<attribute name="Birthday" column="Birthday" type="Date" />
<attribute name="Grade" column="Grade" type="Integer" />
<attribute name="Score" column="Score" type="Decimal" />
</class>
在系统开发中,至于实体是从数据库读取,还是从内存读取,这是完全透明的。
关于内存存储的功能,将会在另外的文章中详细描述。
总结:
以上介绍好SPL中实体的情况,当然,这仅仅是实体本身的功能,大家可能也想到了“我要根据自定义的查询条件来获取记录呢?”
“我要对表中进行批量更新和批量删除呢?”
这些功能就是SPL中的标准(Criteria)的功能,将会在后续的文章中讲述。
听棠
2004年11月