Spring.NET企业架构实践之 Nhibernate + WCF + ASP.NET MVC + NVelocity 对PetShop4.0重构(三)——持久层
什么是持久层?先解释什么是持久,英文persistence,将内存中的数据固化,保持在物理储存设备中。然而在企业应用中,往往通过关系型数据库来完成这一过程。那么持久层的定义是:相对于三层架构中的表示层、业务层而言,专门负责持久化数据的独立领域。 设计模式中的“单一职责”原则确定了分层的目的,说白了,持久层就是专门与数据库打交道的。如图1所示
图1
在PetShop4.0中的DAL(数据库访问层)就是操作数据库的。在其DAL中,通过SQL语句返回DataReader,然后给Model对象赋值;在添加、修改、删除操作中,通过Model对象的数据生成SQL语句,然后写入数据库。此时,我们能够看出每张表都用同样的操作。虽然PetShop4.0使用SqlHelper封装数据库操作,但是却没有一个通用的CRUD封装。目前PetShop4.0每个表都对应各种的CRUD,这样就会出现大量重复的代码。
对于PetShop4.0的使用多钟数据库来言。对于每种数据库都要写一种DAL。这样数据库的可移植性就不强了。
针对上述问题,我使用ORM框架NHibernate作为持久层框架,来取代PetShop4.0的DAL。
什么是ORM?对象关系映射,英文Object Relational Mapping。是解决“阻抗不匹配”的一种技术。“阻抗不匹配”是指,关系型数据库中数据与编程语言中的对象存在差异。使用面向对象的语言编程的同时又要为“面向关系”而犯愁。在企业级的系统开发中,程序员有30%的时间用来解决“阻抗不匹配”的问题,花大量时间去编写SQL语句。而ORM框架的出现就能很好的解决这样的问题,从而做到快速开发。在一个项目的编码过程中,业务层是比较重要的,则编写表示层和持久层代码所用的时间不应该过长。所以使用好一个ORM框架尤为重要。至于为什么要使用ORM框架,有一个重要的原因:提高开发速度,减少开发和维护成本。然而这一点对于一个商业公司来说相当看重。
ORM的O是对象的意思,代表高级语言中的实体(Entity)模型;R是关系的意思,代表关系型数据库;M是映射的意思,表示通过某种方式实现对象和关系型数据库的映射。如图2所示
图2
本系列文章,使用NHibernate作为ORM框架。这样操作对象就相当于操作数据库,开发人员不需要关心SQL语句怎么写,一门心思的专注面向对象的开发,从而使得应用程序更OO(面向对象)。作为NHibernate的实体对象,完全是POJO(上篇文章提到过)。在持久层中,NHibernate框架借助映射文件和实体所承载的数据自动生成SQL语句,实现自动持久化。但是NHibernate仍然存在一些缺点:如,批量删除和修改。至于复杂的查询报表来说,java开发人员往往放弃Hibernate,而使用ibatis或jdbc的方式实现。但是.NET在这一点做的很好,通过NHibernate集成Linq的方式,使我们能够通过Linq的语法查询出想要得到的数据。巧妙的把C#语法(更且却的说是.NET)和java中最流行的框架之一的.NET版NHibernate完美结合起来。可以这么说,HQL(Hibernate Query Language)不是完全的面向对象,但是Linq to NHibernate是觉对的面向对象。
在调用NHibernate的API操作数据库时,每个对象都需要写一个DAO。这里,我们使用基于泛型的Repository(资源库)模式将相同的代码内聚起来。
{
/// <summary>
/// 获取实体
/// </summary>
/// <param name="id">主键</param>
/// <returns></returns>
T Get(object id);
/// <summary>
/// 插入实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
object Save(T entity);
/// <summary>
/// 修改实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
void Update(T entity);
/// <summary>
/// 保存实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
void SaveOrUpdate(T entity);
/// <summary>
/// 删除实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
void Delete(T entity);
/// <summary>
/// 获取全部集合
/// </summary>
/// <returns></returns>
IQueryable<T> LoadAll();
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Spring.Data.NHibernate.Generic.Support;
using NHibernate.Linq;
public abstract class RepositoryBase<T> : HibernateDaoSupport, IRepository<T> where T : class
{
public virtual object Save(T entity)
{
return this.HibernateTemplate.Save(entity);
}
public virtual T Get(object id)
{
return this.HibernateTemplate.Get<T>(id);
}
public virtual IQueryable<T> LoadAll()
{
return this.Session.Linq<T>();
}
public virtual void Update(T entity)
{
this.HibernateTemplate.Update(entity);
}
public void Delete(T entity)
{
this.HibernateTemplate.Delete(entity);
}
public virtual void SaveOrUpdate(T entity)
{
this.HibernateTemplate.SaveOrUpdate(entity);
}
}
这样,同为增、删、改、查的代码,我只需要写一遍就可以了。对于数据库的迁移,可以通过配置数据库方言来实现其目的。在使用Spring.NET作为IoC框架中,我们不用关心和有意去编写数据库事务,并且NHibernate的Session管理也交给Spring.NET来处理。重构后的代码与PetShop4.0的DAL代码比较起来,代码行数减少了很多,但代码的可维护性和灵活性却有明显的提升。