介绍一下本人最近在实现的数据库访问框架,我认为它能够简化数据库访问的复杂性。
别骂我在这个ORM漫天飞的时代,还多搞个ORM出来添乱,我只是希望大家来讨论一下我这个思路。
我暂时给这个框架取名为Pool,其中的核心类叫Fisher。
上次说过ADO.NET在Pool中的简化,这次说ORM部分。简单举个例子,数据库中的实体表:Clazz 班级表
Id int 主键 自动增长
Name nvarchar(50)Student 学生表
Id int 主键 自动增长
Age int
Name nvarchar(50)
ClazzId int 外键关联班级表
程序中的实体类:public class Clazz{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Student> Students { get; set; }//外键学生对象的集合
}
public class Student{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int Age { get; set; }
public virtual Clazz Clazz { get; set; }//外键关联班级
}相比其他很多框架,Pool的一大亮点就是尽量少的配置。
这么明摆的对应,还需要任何多余的配置吗?
如果需要Pool的配置暂时还没有专业的配置文件。
配置写在.NET程序的配置文件中,也就是App.Config或者Web.Config文件中。
在ConnectionStrings节点下添加项<!--配置Clazz实体类对应表名Clazz-->
<add name="Clazz" connectionString="Name=Clazz"/>
<!--配置Clazz.Name对应列名Name-->
<add name="Clazz.Name" connectionString="Name=Name"/>这些配置不是必须的,Pool会自动判断最一般的情况。增://添加一个班级名为"丐班"
Clazz clazz=new Clazz();
clazz.Name="丐班";
Fisher.TheFisher.Add(clazz);Pool会自动从数据库中获取插入的Id,填充到clazz对象中。
因为对sqlserver做了特殊处理。
Pool提供了一套通用的配置办法。<!--sqlserver中-->
<add name="Clazz.Id" connectionString="Getter=@@Identity"/>
<!--oracle中用序列-->
<add name="Clazz.Id" connectionString="Default=sClazzId.nextval;Getter=sClazzId.currval"/>节点中Default表示使用默认值插入,Getter表示如何获取刚才插入的默认值。
也就是生成的sql语句为:--sqlserver
insert Clazz(Name)values(@Age)
select * from Student where Id=@@Identity
--oracle
insert Clazz(Id,Name)values(sClazzId.nextval,:Name)
select * from Student where Id=sClazzId.currval如果用配置方案,对任何数据库通用。
即使要插入的列不是主键,也可以使用数据库的默认值。删://删除clazz对应的数据记录
theFisher.Del(clazz);
//删除Id为1的Clazz数据记录
theFisher.Del<Clazz>(1);Pool提供2套删除策略,1是删除对象,2是根据Id。
删除Clazz的时候,会连带把Clazz对应的Student对象给删了。
(这个连级删除希望大家给点意见,是不是应该连级?)改:clazz.Name="斧头班";
theFisher.Edit(clazz);修改不会导致连级修改,根据我之前在CSDN发的帖子调查,
认同连级修改会造成项目的混乱,也就是修改clazz对象时,
clazz.Students不会进行修改。查://查询Id为1的班级
Clazz clazz=theFisher.Get<Clazz>(1);
//使用sql查询,参数使用方式和ADO.NET部分类似
//(sql,参数名1,参数值1,参数名2,参数值2....)
Clazz clazz=theFisher.First<Clazz>("select * from Clazz where Name=@Name","@Name","斧头班");
//查询集合
IList<Clazz> clazzs=theFisher.Find<Clazz>("select * from Clazz where Name like '%班'");
//使用分页 表示从第10开始,查询10条记录
IList<Clazz> clazzs=theFisher.Find<Clazz>(10,10,"select * from Clazz");Pool没有定义如hibernate的HQL类似的查询语言,
使用的是sql查询,虽然有备OO,但是我认为这样提供了更加强大的查询优化空间。Pool中使用懒加载,如查询出student,那么student.clazz不会马上被加载,只有用到才会加载。
这样在关系复杂的数据库中提高了不至于查询一个实体,关联出大半个数据库来。有时候不需要查询全部的字段,如果硬要将实体整个查处,对于性能是浪费。//只查询了student的Id和年龄
IList<Student> students=theFisher.Find<Student>("select Id,Age from Student");这样Pool只会填充Id,Age字段信息。你也可以用存储过程查询:
设有存储过程proc1,内容如下:select * from Student那么Pool也能够:IList<Student> students=theFisher.Find<Student>("proc1");
大致介绍到这里吧
别骂我在这个ORM漫天飞的时代,还多搞个ORM出来添乱,我只是希望大家来讨论一下我这个思路。
我暂时给这个框架取名为Pool,其中的核心类叫Fisher。
上次说过ADO.NET在Pool中的简化,这次说ORM部分。简单举个例子,数据库中的实体表:Clazz 班级表
Id int 主键 自动增长
Name nvarchar(50)Student 学生表
Id int 主键 自动增长
Age int
Name nvarchar(50)
ClazzId int 外键关联班级表
程序中的实体类:public class Clazz{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Student> Students { get; set; }//外键学生对象的集合
}
public class Student{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int Age { get; set; }
public virtual Clazz Clazz { get; set; }//外键关联班级
}相比其他很多框架,Pool的一大亮点就是尽量少的配置。
这么明摆的对应,还需要任何多余的配置吗?
如果需要Pool的配置暂时还没有专业的配置文件。
配置写在.NET程序的配置文件中,也就是App.Config或者Web.Config文件中。
在ConnectionStrings节点下添加项<!--配置Clazz实体类对应表名Clazz-->
<add name="Clazz" connectionString="Name=Clazz"/>
<!--配置Clazz.Name对应列名Name-->
<add name="Clazz.Name" connectionString="Name=Name"/>这些配置不是必须的,Pool会自动判断最一般的情况。增://添加一个班级名为"丐班"
Clazz clazz=new Clazz();
clazz.Name="丐班";
Fisher.TheFisher.Add(clazz);Pool会自动从数据库中获取插入的Id,填充到clazz对象中。
因为对sqlserver做了特殊处理。
Pool提供了一套通用的配置办法。<!--sqlserver中-->
<add name="Clazz.Id" connectionString="Getter=@@Identity"/>
<!--oracle中用序列-->
<add name="Clazz.Id" connectionString="Default=sClazzId.nextval;Getter=sClazzId.currval"/>节点中Default表示使用默认值插入,Getter表示如何获取刚才插入的默认值。
也就是生成的sql语句为:--sqlserver
insert Clazz(Name)values(@Age)
select * from Student where Id=@@Identity
--oracle
insert Clazz(Id,Name)values(sClazzId.nextval,:Name)
select * from Student where Id=sClazzId.currval如果用配置方案,对任何数据库通用。
即使要插入的列不是主键,也可以使用数据库的默认值。删://删除clazz对应的数据记录
theFisher.Del(clazz);
//删除Id为1的Clazz数据记录
theFisher.Del<Clazz>(1);Pool提供2套删除策略,1是删除对象,2是根据Id。
删除Clazz的时候,会连带把Clazz对应的Student对象给删了。
(这个连级删除希望大家给点意见,是不是应该连级?)改:clazz.Name="斧头班";
theFisher.Edit(clazz);修改不会导致连级修改,根据我之前在CSDN发的帖子调查,
认同连级修改会造成项目的混乱,也就是修改clazz对象时,
clazz.Students不会进行修改。查://查询Id为1的班级
Clazz clazz=theFisher.Get<Clazz>(1);
//使用sql查询,参数使用方式和ADO.NET部分类似
//(sql,参数名1,参数值1,参数名2,参数值2....)
Clazz clazz=theFisher.First<Clazz>("select * from Clazz where Name=@Name","@Name","斧头班");
//查询集合
IList<Clazz> clazzs=theFisher.Find<Clazz>("select * from Clazz where Name like '%班'");
//使用分页 表示从第10开始,查询10条记录
IList<Clazz> clazzs=theFisher.Find<Clazz>(10,10,"select * from Clazz");Pool没有定义如hibernate的HQL类似的查询语言,
使用的是sql查询,虽然有备OO,但是我认为这样提供了更加强大的查询优化空间。Pool中使用懒加载,如查询出student,那么student.clazz不会马上被加载,只有用到才会加载。
这样在关系复杂的数据库中提高了不至于查询一个实体,关联出大半个数据库来。有时候不需要查询全部的字段,如果硬要将实体整个查处,对于性能是浪费。//只查询了student的Id和年龄
IList<Student> students=theFisher.Find<Student>("select Id,Age from Student");这样Pool只会填充Id,Age字段信息。你也可以用存储过程查询:
设有存储过程proc1,内容如下:select * from Student那么Pool也能够:IList<Student> students=theFisher.Find<Student>("proc1");
大致介绍到这里吧
怎么写的比手写ADO.NET没差这张贴子才是Pool的ORM部分。
http://topic.csdn.net/u/20100328/18/12fdb814-3882-4613-be85-4b17b05dd4ab.html?seed=815559937&r=64634534#r_64634534 [推荐] 不使用反射的实体类,大家评评?
http://topic.csdn.net/u/20100125/18/d6738f2f-5581-4c8c-8c82-9bd8c2f256c8.html
谢谢你的建议。你的意思是说多家一个属性判断是否要删除嘛?
这个做法好像类似hibernate可配置的。不过我想在想是不是吧判断这部分留给业务逻辑判断呢?
Privilege(PrivalegeID, PrivilegeName)
RolePrivilge(RoleID, PrivalegeID)这个类要怎么设计, 还有删除角色或权限的时候怎么自动把第三个表里的记录清除。
你这个问题值得考虑。我不喜欢使用多对多关联,我会分解成一对多,和多对一的。
所以在类的设计上和一般的情况不会有差别。也就是class Role
RoleID
RoleName
//加一个外键集合
IList<RolePrivilge> RolePrivilgesclass Privilege
PrivalegeID,
PrivilegeName
//也有一个外键集合
IList<RolePrivilge> RolePrivilgesclass RolePrivilge
//使用外键引用
Role
Privileg
大概也就是分解成一对多和多对一,
然后原来什么样现在还是什么样。
简单
少配置(大多数情况不需要配置)
懒加载
高效率(效率比普通手写ADO.NET相当,甚至更优秀)
能够使用原始sql
就访问数据库是否简单来看,
ObjectDataSource数据源控件+低层强类型的DataSet,
已经可以做到不写一行代码,实现对表的增,删,改,查!
多简单。
一个项目下来,这样能满足所有需求吗?
Pool绝不是仅仅为了简单。我想很多人认为这又是再造轮子了,
但是我觉得,这个轮子和其他轮子是不同的。
这个轮子有别的轮子不具备的优势,
或是与别的轮子各有所长。
多表操作,存储过程,事务等都完全可以!
比传统的代码方式大概减少50%的代码量吧!
我仅仅从简单角度看,并不是否认楼主的追求!
你也可以看看,Pool和强类型的DataSet比较一下,我想一定各有优势的
就图形方式这点就比传统的代码方式写DAL强啊!
1.级联删除是由业务决定的,不是由程序决定的2.其实你写实体表达业务模型,我认为有几点不合适
a.重复写一遍(数据库表已经存在,其实表和实体是一个东西,一个存在在数据库,一个在内存中)
b.Clazz和Student之间关系,不能反找,可以通过Student可以找到Clazz,但是你在查询中无法通过Clazz找到Student,有人说可以在Clazz增加一个 List<Student>属性,这样是可以的,但是你做了一个没有意思的工作,同时增加复杂性,遇到一种这样的情况就要做这种处理
c.将数据转换为实体模型,需要消耗性能,所有hibernate有个问题就是性能问题
楼主,我改进了一下PDF.NET框架,下面是测试结果:19291104 优化过的ado.net
22091263 一般的ado.net
26241501 pool简化
73604210 反射
46932684 使用DateTable
20331162 linq ms:
20081148 pool查询
15160868 pdf ms:
----------------------------
现在PDF.NET 比LINQ还快,比你的pool查询也快,其实秘密就在这里:
public static List<MyNameSpace.Table1> QueryList(System.Data.IDataReader reader)
{
List<MyNameSpace.Table1> list = new List<MyNameSpace.Table1>();
using (reader)
{ if (reader.Read())
{
int fcount = reader.FieldCount;
string[] names = new string[fcount];
object[] values = new object[fcount]; for (int i = 0; i < fcount; i++)
names[i] = reader.GetName(i); do
{
reader.GetValues(values); MyNameSpace.Table1 t = new MyNameSpace.Table1();
t.PropertyNames = names;
t.PropertyValues = values; list.Add(t);
} while (reader.Read()); }
}
return list;
}
而Pool的性能你大可放心,比一般的手写Ado.Net更快,因为Pool运行时动态编译,并且做过优化。
呵呵,恭喜你拉。
你这个办法确实很高,
效率应该是可以提升了。
但是访问实体的时候,
仍然要面对缓慢的hash的。
有些把效率转移开的意思。