IDBAccesser接口是XCodeFactory生成的数据层代码的核心部分。要了XCodeFactory生成的数据层,就必须了解 IDBAccesser接口。
如果你看过上一节的示例、并研究过生成的代码,你一定会发现,所有对数据库的访问操作都是通过IDBAccesser接口来完成的。比如,上一节插入一个学生到数据库中大致是这样的:
其中的成员变量dealStudent就是IDBAccesser接口的引用。在看看StudentDealer.cs文件中的代码,会发现三个类:StudentDealer、StudentSqlDealer、StudentOleDealer,它们的继承关系一目了然。其中StudentDealer直接从IDBAccesser继承,并且它是一个抽象类,之所以设计为abstract,是因为它是与数据库类型无关的,而与数据库类型相关的信息都封装在几个虚拟方法中了,比如GetDBTypeElementFactory方法,而与数据库类型相关的实现类,如StudentSqlDealer和StudentOleDealer将会覆写这些虚拟方法。这样做的好处是,如果增加对其它数据库类型的支持,只需要增加一个派生类,比如需要支持Oracle,那么就增加一个StudentOracleDealer,而这个类的实现也是非常简洁的,只要覆盖基类StudentDealer的四个虚拟方法即可。
下面给出以IDBAccesser为核心的继承体系图:
可以看到,IDBAccesser从另外三个接口继承。你对数据库表的所有操作基本上都包含于这三个接口中。这三个接口分别是:基于对象操作的查询接口IDBAccesserQuery,基于对象操作的命令接口IDBAccesserOrder(添加/删除/修改)、和基于关系操作的关系型接口IDBAccesserRelation(操作DataSet、DataReader ,基于关系操作)。下面将展示这三个接口的外貌。
(1)IDBAccesserQuery接口
大家已经发现了, 查询返回的对象类型是object,而且后面的各个接口的操作也都是object类型,这种弱类型的方式是迫不得已的,因为需要统一所有数据访问类的基础接口,在.NET 1.1中没有提供对泛型的支持,所以在使用通用的object类型。
查询的条件一般是where字句,它符合SQL语句标准。我看到很多ORM工具,都提供了自己的查询语句,那可能是为了更好的实现它自己的目的。而XCodeFactory没有这样的要求,因为标准的sql语句已经完全够用,并且可以降低使用者的上手难度--相信普通的开发人员都使用过SQL进行数据库操作。
注意,如果没有where条件,那么参数请传入"",而不是传入null。
GetObjectsWithoutBlob方法和FillBlobData方法是对Blob字段的延迟加载机制进行支持。比如,我们的Mentor表中需要存放每个导师的照片资料,这是一个Blob字段。在浏览导师列表的时候,如果一次把所有的Blob都读出来,无疑会浪费内存并损害访问数据库的效率。只有当用户想深入查看某个导师的详细资料时,在调用FillBlobData方法来填充该导师照片字段的数据,这样不是更好么?!
最后一个方法 GetDataSet应该属于一个关系型操作,但是它也属于查询操作,所以就将它放在IDBAccesserQuery接口中了。
(2)IDBAccesserOrder接口
一般,查询是与事务没有关系的,但是命令就经常会牵涉到事务,所以IDBAccesserOrder接口一开始就对事务提供了基本的支持,IDBAccesserOrder接口中的各个方法的第二个参数就是事务的引用。如果你某个命令操作不牵涉事务,那么第二个参数可以传入null。Insert和Update方法都很简单,而Delete方法中的第一个参数为object类型的ID,不知你是否还记得在XCF约定中有一条那就是每个表需要有一个主键字段,并且字段名为“ID” ,Delete方法便依赖于这个约定!ID的类型通常为数值类型(如int)或字符串。
再看InsertBatch方法,它是执行批插入的功能。很多情况下,我们的批插入都需要与一个事务关联起来,InsertBatch方法正好简化了这个操作。请注意,InsertBatch与加for循环的Insert方法的效率是不一样的,InsertBatch的效率更高!
最后一个方法InsertReturnIdentity方法用于主键ID是自动编号的情况。在主键是自动编号时,插入一个对象之前,该对象的主键字段ID的值是不知道的,只有插入后,这个值才确定下来。InsertReturnIdentity使你插入对象的操作一完成,即刻就可以知道数据库为该字段分配的ID值。这个功能是很有用的:)
(3)IDBAccesserRelation接口
如果要获取数据库中某条记录的某个字段的值,可以这样做:
而使用IDBAccesserRelation接口就可以这样做:
简单多了吧。GetFieldValueEx与GetFieldValue的区别在于,GetFieldValue通过ID取出指定的对象,而GetFieldValueEx取出的是符合where字句条件的第一个对象的特定字段的值。
UpdateFieldValue也是一样,它为更新指定对象的某特定字段的值提供了简洁入口。
其它的界个方法就不用多说了。
最后来看看IDBAccesser接口吧
IDBAccesser除了继承上述的三个接口外,还新添加了几个属相和方法。前面三个属性很容易理解,最后一个方法GetPaginationMgr用于获取分页管理器,分页管理器对数据集自动分页提供支持,关于它的详细介绍将会在本系列的“高级特性”一文中。
到此为止,我们已经了解了IDBAccesser接口的所有内容,在熟悉了IDBAccesser接口之后,相信你可以很容易的使用XCodeFactory的生成数据层的代码了。
为了更加简洁的使用数据层代码,请关注下一篇文章:DataEntrance简化数据访问
XCodeFactory3.0完全攻略 目录
如果你看过上一节的示例、并研究过生成的代码,你一定会发现,所有对数据库的访问操作都是通过IDBAccesser接口来完成的。比如,上一节插入一个学生到数据库中大致是这样的:
private IDBAccesser dealStudent = DataEntrance.CreateDBAccesser(typeof(Student)) ;
Student a_student = this.CreateStudentFromTextbox() ;
this.dealStudent.Insert(a_student , null) ;
Student a_student = this.CreateStudentFromTextbox() ;
this.dealStudent.Insert(a_student , null) ;
其中的成员变量dealStudent就是IDBAccesser接口的引用。在看看StudentDealer.cs文件中的代码,会发现三个类:StudentDealer、StudentSqlDealer、StudentOleDealer,它们的继承关系一目了然。其中StudentDealer直接从IDBAccesser继承,并且它是一个抽象类,之所以设计为abstract,是因为它是与数据库类型无关的,而与数据库类型相关的信息都封装在几个虚拟方法中了,比如GetDBTypeElementFactory方法,而与数据库类型相关的实现类,如StudentSqlDealer和StudentOleDealer将会覆写这些虚拟方法。这样做的好处是,如果增加对其它数据库类型的支持,只需要增加一个派生类,比如需要支持Oracle,那么就增加一个StudentOracleDealer,而这个类的实现也是非常简洁的,只要覆盖基类StudentDealer的四个虚拟方法即可。
下面给出以IDBAccesser为核心的继承体系图:
可以看到,IDBAccesser从另外三个接口继承。你对数据库表的所有操作基本上都包含于这三个接口中。这三个接口分别是:基于对象操作的查询接口IDBAccesserQuery,基于对象操作的命令接口IDBAccesserOrder(添加/删除/修改)、和基于关系操作的关系型接口IDBAccesserRelation(操作DataSet、DataReader ,基于关系操作)。下面将展示这三个接口的外貌。
(1)IDBAccesserQuery接口
/// <summary>
/// IDBAccesserQuery 为执行sql查询接口
/// </summary>
public interface IDBAccesserQuery
{
bool ReviseAObject(string where_str ,object target ) ;//使用数据库内容来更新当前对象
object GetAObject(string where_str) ;//if there is no condition clause ,please input ""
object[] GetObjects(string where_str) ;
object[] GetObjectsWithoutBlob(string where_str) ; //获取的对象中凡是Blob字段都未填充
bool FillBlobData(object obj) ; //填充某个对象的所有Blob字段
DataSet GetDataSet(string select_str) ;
}
/// IDBAccesserQuery 为执行sql查询接口
/// </summary>
public interface IDBAccesserQuery
{
bool ReviseAObject(string where_str ,object target ) ;//使用数据库内容来更新当前对象
object GetAObject(string where_str) ;//if there is no condition clause ,please input ""
object[] GetObjects(string where_str) ;
object[] GetObjectsWithoutBlob(string where_str) ; //获取的对象中凡是Blob字段都未填充
bool FillBlobData(object obj) ; //填充某个对象的所有Blob字段
DataSet GetDataSet(string select_str) ;
}
大家已经发现了, 查询返回的对象类型是object,而且后面的各个接口的操作也都是object类型,这种弱类型的方式是迫不得已的,因为需要统一所有数据访问类的基础接口,在.NET 1.1中没有提供对泛型的支持,所以在使用通用的object类型。
查询的条件一般是where字句,它符合SQL语句标准。我看到很多ORM工具,都提供了自己的查询语句,那可能是为了更好的实现它自己的目的。而XCodeFactory没有这样的要求,因为标准的sql语句已经完全够用,并且可以降低使用者的上手难度--相信普通的开发人员都使用过SQL进行数据库操作。
注意,如果没有where条件,那么参数请传入"",而不是传入null。
GetObjectsWithoutBlob方法和FillBlobData方法是对Blob字段的延迟加载机制进行支持。比如,我们的Mentor表中需要存放每个导师的照片资料,这是一个Blob字段。在浏览导师列表的时候,如果一次把所有的Blob都读出来,无疑会浪费内存并损害访问数据库的效率。只有当用户想深入查看某个导师的详细资料时,在调用FillBlobData方法来填充该导师照片字段的数据,这样不是更好么?!
最后一个方法 GetDataSet应该属于一个关系型操作,但是它也属于查询操作,所以就将它放在IDBAccesserQuery接口中了。
(2)IDBAccesserOrder接口
/// <summary>
/// IDBAccesserOrder 为执行sql命令接口
/// </summary>
public interface IDBAccesserOrder
{
//如果不需要事务,trans以null传入
void Insert(object obj ,IDbTransaction trans) ;
void Update(object obj ,IDbTransaction trans) ;
void Delete(object ID ,IDbTransaction trans) ; //ID一般为int或string类型
void InsertBatch(ArrayList objs ,IDbTransaction trans) ;//批插入
//插入对象并返回自动编号标志
object InsertReturnIdentity(object obj ,IDbTransaction trans) ;
}
/// IDBAccesserOrder 为执行sql命令接口
/// </summary>
public interface IDBAccesserOrder
{
//如果不需要事务,trans以null传入
void Insert(object obj ,IDbTransaction trans) ;
void Update(object obj ,IDbTransaction trans) ;
void Delete(object ID ,IDbTransaction trans) ; //ID一般为int或string类型
void InsertBatch(ArrayList objs ,IDbTransaction trans) ;//批插入
//插入对象并返回自动编号标志
object InsertReturnIdentity(object obj ,IDbTransaction trans) ;
}
一般,查询是与事务没有关系的,但是命令就经常会牵涉到事务,所以IDBAccesserOrder接口一开始就对事务提供了基本的支持,IDBAccesserOrder接口中的各个方法的第二个参数就是事务的引用。如果你某个命令操作不牵涉事务,那么第二个参数可以传入null。Insert和Update方法都很简单,而Delete方法中的第一个参数为object类型的ID,不知你是否还记得在XCF约定中有一条那就是每个表需要有一个主键字段,并且字段名为“ID” ,Delete方法便依赖于这个约定!ID的类型通常为数值类型(如int)或字符串。
再看InsertBatch方法,它是执行批插入的功能。很多情况下,我们的批插入都需要与一个事务关联起来,InsertBatch方法正好简化了这个操作。请注意,InsertBatch与加for循环的Insert方法的效率是不一样的,InsertBatch的效率更高!
最后一个方法InsertReturnIdentity方法用于主键ID是自动编号的情况。在主键是自动编号时,插入一个对象之前,该对象的主键字段ID的值是不知道的,只有插入后,这个值才确定下来。InsertReturnIdentity使你插入对象的操作一完成,即刻就可以知道数据库为该字段分配的ID值。这个功能是很有用的:)
(3)IDBAccesserRelation接口
/// <summary>
/// IDBAccesserRelation 为执行sql关系型接口
/// </summary>
public interface IDBAccesserRelation
{
//RelationAction
int GetRecordsCount() ;//得到表中记录的总数
object GetFieldValue (string theID ,string fieldName) ;
object GetFieldValueEx(string whereStr ,string fieldName) ;
bool UpdateFieldValue(object theID ,string fieldName ,object newVal ,IDbTransaction trans) ;
object ExecuteScalar(string command) ;
IDataReader GetReader(string select_str) ; //IDataReader用完后要及时关闭
}
/// IDBAccesserRelation 为执行sql关系型接口
/// </summary>
public interface IDBAccesserRelation
{
//RelationAction
int GetRecordsCount() ;//得到表中记录的总数
object GetFieldValue (string theID ,string fieldName) ;
object GetFieldValueEx(string whereStr ,string fieldName) ;
bool UpdateFieldValue(object theID ,string fieldName ,object newVal ,IDbTransaction trans) ;
object ExecuteScalar(string command) ;
IDataReader GetReader(string select_str) ; //IDataReader用完后要及时关闭
}
string whereStr = string.Format("where {0} = '{1}'" ,Student._ID ,"1001") ;
Student a_student = (Student)this.dealStudent.GetAObject(whereStr) ;
int hisAge = a_student.Age ;
Student a_student = (Student)this.dealStudent.GetAObject(whereStr) ;
int hisAge = a_student.Age ;
int hisAge = (int)this.dealStudent.GetFieldValue("1001" ,Student._Age) ;
UpdateFieldValue也是一样,它为更新指定对象的某特定字段的值提供了简洁入口。
其它的界个方法就不用多说了。
最后来看看IDBAccesser接口吧
/// <summary>
/// IDBAccesser 是最核心的数据访问接口
/// 作者:朱伟 sky.zhuwei@163.com
/// </summary>
public interface IDBAccesser :IDBAccesserQuery ,IDBAccesserOrder ,IDBAccesserRelation
{
//property
string ConnectString{get ;}
string DbTableName {get ;}
DataBaseType DataBaseType{get ;}
//others
IDataPaginationManager GetPaginationMgr(string selectStr ,int page_size ,bool ascending) ;
}
/// IDBAccesser 是最核心的数据访问接口
/// 作者:朱伟 sky.zhuwei@163.com
/// </summary>
public interface IDBAccesser :IDBAccesserQuery ,IDBAccesserOrder ,IDBAccesserRelation
{
//property
string ConnectString{get ;}
string DbTableName {get ;}
DataBaseType DataBaseType{get ;}
//others
IDataPaginationManager GetPaginationMgr(string selectStr ,int page_size ,bool ascending) ;
}
IDBAccesser除了继承上述的三个接口外,还新添加了几个属相和方法。前面三个属性很容易理解,最后一个方法GetPaginationMgr用于获取分页管理器,分页管理器对数据集自动分页提供支持,关于它的详细介绍将会在本系列的“高级特性”一文中。
到此为止,我们已经了解了IDBAccesser接口的所有内容,在熟悉了IDBAccesser接口之后,相信你可以很容易的使用XCodeFactory的生成数据层的代码了。
为了更加简洁的使用数据层代码,请关注下一篇文章:DataEntrance简化数据访问
XCodeFactory3.0完全攻略 目录