现在的位置: 首页 > 综合 > 正文

企业开发框架NHibernate和Spring.Net -2

2013年12月10日 ⁄ 综合 ⁄ 共 12293字 ⁄ 字号 评论关闭
文章目录
 

3.NHibernate映射

对象和关系数据库之间的映射是用一个XML文档(XML
document)来定义的。这个映射文档被设计为易读的,并且可以手工修改。映射语言是以.NET为中心的,意味着映射是按照持久化类的定义来创建的,
而非表的定义。在Hibernate中XML映射文档可以手动定义,也有一些工具来辅助生成,包括Xdoclet、Middlegen和
AndroMDA,但是在NHibernate文档中并没有上述的辅助工具,不过可以采用MyGeneration这样的代码生成工具来生成XML配置文
档。下面是一个映射的例子:

#001 <?xml version="1.0" ?>

#002 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" namespace="Eg" assembly="Eg">

#003 <class name="Cat" table="CATS" discriminator-value="C">

#004      <id name="Id" column="uid" type="Int64">

#005         <generator class="hilo"/>

#006      </id>

#007      <discriminator column="subclass" type="Char"/>

#008      <property name="Name" type="Char"/>

#009      <property name="Color" not-null="false"/>

#010      <property name="Sex" not-null="false" update="false"/>

#011      <property name="Weight"/>

#012      <many-to-one name="Mate" column="mate_id"/>

#013      <set name="Kittens">

#014         <key column="mother_id"/>

#015         <one-to-many class="Cat"/>

#016      </set>

#017      <subclass name="DomesticCat" discriminator-value="D">

#018         <property name="Name" type="String"/>

#019      </subclass>

#020 </class>

#021 </hibernate-mapping>

所有的XML映射都需要使用nhibernate-mapping-2.0
schema。目前的schema可以在NHibernate的资源路径或者是NHibernate.dll的嵌入资源(Embedded
Resource)中找到。NHibernate总是会优先使用嵌入在资源中的schema文件。在使用VisualStudio.NET时,用户应该将
hibernate-mapping拷贝到C:/Program Files/Microsoft Visual Studio .NET
2003/Common7/ Packages/schemas/xml路径中,以获得智能感知功能。

#002行:hibernate-mapping这个元素包括4个可选的属性。schema属性,指明了这个
映射所引用的表所在的schema名称。假若指定了这个属性,表名会加上所指定的schema的名字扩展为全限定名。假若没有指定,表名就不会使用全限定
名。default-cascade指定了未明确注明cascade属性的属性和集合类会采取什么样的默认级联风格。auto-import属性默认在查
询语言中可以使用非全限定名的类名。default-access告诉用户怎么访问属性值。假若有两个持久化类,它们的非全限定名是一样的,应该设置
auto-import= "false"。假若把一个“import”过的名字同时对应两个类,NHibernate会抛出一个异常。

#003行:使用class元素来定义一个持久化类。name是持久化类(或者接口)的全限定名。table是对应的数据库表名。discriminator-value(可选 - 默认和类名一样)是一个用于区分不同的子类的值,在有多态行为时使用。

#004行:使用id来标识主键字段,因为被映射的类必须声明对应数据库表主键字段。大多数类有一个属性,为每一个实例包含唯一的标识。<id> 元素定义了该属性到数据库表主键字段的映射。

#005行:必须声明的 <generator>
子元素是一个.NET类的名字,用来为该持久化类的实例生成唯一的标识。其中hilo(高低位)使用一个高/低位算法来高效地生成Int64、Int32
或者Int16类型的标识符。给定一个表和字段(默认分别是hibernate_unique_key
和next)作为高位值的来源。高/低位算法生成的标识符只在特定的数据库中是唯一的。

#007行:在“一棵对象继承树对应一个表”的策略中,<discriminator>元素是必
需的,它声明了表的识别器字段。识别器字段包含标志值,用于告知持久化层应该为某个特定的行创建哪一个子类的实例。只能使用如下受到限制的类型:
String,Char,Int32,Byte,Int16,Boolean,YesNo,TrueFalse。

#008-011行:<property>元素为类声明了一个持久化的属性。name是属性的名
字,type(可选)是一个NHibernate类型的名字,not-null(可选)表明这个属性是否为空,update(可选-默认为true)表明
在用于UPDATE的SQL语句中是否包含这个字段。

#012行:通过many-to-one元素,可以定义一种常见的与另一个持久化类的关联。这种关系模型是多对一关联(实际上是一个对象引用)。

#013-016行:<set>为集合类标识,其中discriminator-value(可选 - 默认为类名)用于区分每个独立的子类的值。

#017行:子类(subclass),多态持久化需要为父类的每个子类都进行声明。对于上面建议的“每一棵类继承树对应一个表”的策略来说,就需要使用<subclass>声明。

4.NHibernate应用实例

上面已经编写好了Cat类和相应的映射文件,为了正确调用Nhibernate,必须对web.config配置文件进行配置。具体配置代码如下:

#001 <xml version="1.0" encoding="utf-8" ?>

#002 <configuration>

#003       <configSections>

#004           <section name="nhibernate"
type="System.Configuration.NameValueSectionHandler, System
Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

#005       </configSections>

#006       <nhibernate>

#007           <add

#008                key="hibernate.connection.provider"

                      value="NHibernate.Connection.DriverConnectionProvider" />

#009           <add

#010                key="hibernate.connection.driver_class"

                     value="NHibernate.Driver.SqlClientDriver" />

#011           <add

#012                key="hibernate.connection.connection_string"

                     value="server=localhost;uid=sa;pwd=123;database=Sample" />

#013           <add

#014                key="hibernate.connection.isolation" value="ReadCommitted" />

#015           <add

#016                key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />

#017       </nhibernate>

#018 </configuration>

#012行:hibernate.connection.connection_string的value为连接服务器ConnectoinString。其他的都是Nhibernate运行所必需的配置信息。

配置完web.config文件,在测试工程中引用NHibernate.dll和Eg,具体代码如下。

#001    using System;

#002    using System.Collections.Generic;

#003    using System.Text;

#004   using NHibernate;

#005    using NHibernate.Cfg;

#006    using POJOLibrary;

#007    namespace Test

#008    {

#009        class Program

#010        {

#011            static void Main(string[] args)

#012            { Configuration c = new Configuration();

#013                c.AddAssembly("Eg");

#014                ISessionFactory factory = c.BuildSessionFactory();

#015               ISession s = factory.OpenSession();

#016                ITransaction t = s.BeginTransaction();

#017                Cat cat = new Cat();

#018                cat.Name = “Bolo”;

#019                cat.Color = “Black”;

#020                cat.Sex = “Male” ;

#021                cat.Weight = “12” ;

#022                 s.Save(cat);

#023                t.Commit();}

#024        }

#025    }

NHibernate是通过Isession的实现类来提供功能的,上面代码通过Itransaction打开一个事务t=s.BeginTransaction(),最后通过调用t.Commit()提交事务,将数据写入数据库中。

通过上述NHibernate的一个简单的应用实例可以看到,整个应用程序中没有写任何一句SQL语句。

虽然NHibernate目前不是非常的成熟,还没有达到实用化的水平,它经常在负荷有些大的时候崩溃,但是
它脱胎于在Java社区有着巨大影响力的Hibernate,而且NHibernate在实际应用方面确实体现出了它的优势,基于上述原因
NHibernate一定会被越来越多的开发人员所接受,而且一定会被标准化。到目前为止,NHibernate的最新版本为1.1,官方站点http:
//nhibernate.sourceforge.net/上目前的版本是beta 0.7.0.0。

1.5.3 Spring.Net介绍

Spring.Net是一个关注于.NET企业应用开发的应用程序框架,它能够提供非常丰富的功能,例如依赖
注入(Dependency Injection),面向方面编程(Aspect Oriented
Programming),数据访问抽象以及ASP.NET集成等。Spring.NET脱胎于Java的Spring框架,其1.0版包括了一个功能完
整的反转控制容器和AOP函数库,在后续的版本中将包含对ASP.NET、Remoting和数据访问的支持。Spring.Net的框架如图1-53所
示。

图1-53 Spring.Net的框架图

1.控制反转

控制反转
(IoC,Inversion of Control
)意味着将设计好的
交给系统去控制,而不是在自己的类内部控制。

IoC是近年来兴起的一种思想,主要是协调各组件
间相互的依赖关系,同时大大提高了组件的可移植性,组件的重用机会也变得更多。在传统的实现中,由程序

部代码来控制程序之间的关系。我们经常使用new关键字来实现两组键间关系的组合,这种实现地方式会造成组件之间耦合(一个好的设计,不但要实现代码重
用,还要将组件间关系解耦)。IoC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态
注入组件中。

分离关注(Separation of Concerns:SOC)是产生IoC模式和AOP
的最原始动力,通过功能分解可得到关注点,这些关注可以是组件、方面或服务。

GOF设计模式
中,我们已经习惯一种思维编程方式——接口驱动。接口驱动有很多好处,可以提供灵活的子类实现,增加代码稳定和健壮性等,但是接口一定是需要实现的,即如下语句一定要执行。

AInterface a = new AInterfaceImp();

AInterfaceImp是接口AInterface的子类,Ioc模式可以根据需要延缓接口的实现,有个比喻:接口如同空的模型套,在必要时向模型套注射石膏,成为一个模型实体。可以人为控制接口的实现完成“注射”。

IoC的实现方式有以下几种:

*
    基于接口的(Interface-based IoC,Type-1)。

*
    基于设值的(Setter-based IoC,Type-2)。

*
    基于构造的(Construtor-based IoC,Type-3)。

下面通过简单的例子分别介绍上述几种实现方式。

(1)Type-1。基于接口的设计方法通常是利用接口将调用者与实现者分离。

#001 public class Sport {

#002 private InterfaceBall ball; //InterfaceBall是定义的接口

#003 public void init() {

#004 //Basketball实现了InterfaceBall接口

#005 ball = (InterfaceBall) Class.forName("Basketball").newInstance();}

#006 }

Sport类在编译期依赖于InterfaceBall的实现,为了将调用者与实现者分离,可以动态生成Basketball类并将其强制类型转换为InterfaceBall。

(2)Type-2。基于设值的设计方法是通过在类中暴露setter方法来实现依赖关系。

#001 public class Sport {

#002 private InterfaceBall _ball;

#003 public InterfaceBal1 ball

#004 {

#005     set{ _ball = value ;} }

#006 }

Spring.NET就是实现了该类型的轻量级容器。

(3)Type-3。通过构造方法完成依赖关系。

#001 public class Sport {

#002 private InterfaceBall ball;

#003 public Sport(InterfaceBall arg) {

#004 ball = arg; }

#005 }

由于Type-3在构造期就形成了对象的依赖关系,所以对对象的重用变得困难。有些框架需要组件提供一个默认
的构造方法,此时就显现出Type-3的局限性。通常所有的参数都是通过构造方法注入的,当对象间的依赖关系较多时,构造方法就显得比较复杂,不利于单元
测试。PicoContainer就是实现了Type-3依赖注入模式的轻量级容器。

2.Spring.NET库

Spring.NET库有6个基本组成部分,基本上涵盖了Spring.NET框架的所有功能结构。

*
   
Spring.Core库是Spring.NET框架最基础的部分,它提供了依赖注入的功能。Spring.NET中大部分的函数库都依赖于这个核心库提
供的功能,或者是对核心库的扩展。IObjectFactory是核心容器接口,负责管理容器内的注入对象,而IApplicationContext则
是IObjectFactory的继承,它扩展了一些功能。

*
    Spring.Aop库为商业逻辑对象提供了面向方面编程的支持,它为创建企业应用和为商业对象提供服务打下了基础,是Spring核心库中IoC容器的补充。

*
    Spring.Web库为ASP.NET增加了很多功能,例如ASP.NET页面的依赖注入,数据双向绑定,为ASP.NET提供母版页功能,增强了本地化支持。所有的这些都是对ASP.NET很好的扩展。

*
   
Spring.Services库可以将任何一个“普通”对象(“普通”是指该对象不是继承自特殊服务的基类)暴露成为一个企业应用(COM+)或者远程
对象。因为对依赖注入和原数据属性重载的支持,.NET 中的Web服务会获得更好的配置上的灵活性。同样,该库也提供了对Windows服务的支持。

*
    Spring.Data库为.NET提供了一个数据访问层的抽象,它能够用于从ADO.NET到多种ORM Provider的数据访问提供者。它同时包含了一个ADO.NET抽象层,简化了对ADO.NET的编码和事务管理。

*
    Spring.ORM库提供了一个用于常见的对象—关系映射库的综合层,它提供了诸如对事务管理的支持等功能。

3.面向方面编程(AOP)

面向方面编程是对面向对象编程(OOP)的补充,是另外一种思考编程框架的方法。面向对象是将应用分解成具有
层次结构的对象;而面向方面编程则是把程序分解成方面或者关注点,使诸如事务管理等方面的模块化成为可能。Spring.NET中很关键的一个组件就是
AOP框架。能够帮助Spring.NET的 IoC容器为企业应用提供一个非常强大的中间件解决方案。

AOP用于Spring.NET可以完成下列功能。

*
    提供公开的企业服务,尤其是作为COM+公开服务的替代者。这些服务中最重要的服务是公开的事务管理,这是Spring.NET事务抽象的基础。

*
    允许用户实现定制的方面,通过面向方面编程来补充面向对象编程的不足。

用户不但可以把Spring.NET AOP看作是能够不通过COM+就可以提供公开事务管理的技术,而且还可以充分发挥Spring.NET AOP框架的功能区实现定制方面。

通过上面的介绍读者可能对AOP已经有了一个大致的了解,下面介绍几个关于AOP的概念。

*
    方面(Aspect):这个是一个让读者感觉比较模糊的概念,它和通常意义上的方面不完全一样,它是对关注点的模块化,这可能会横切多个对象。事务管理是一个非常好的横切关注点企业应用的例子。在Spring.NET中,方面作为建议者或者监听器的形式实现。

*
    连接点(Jointpoint):程序运行期间的一些点,例如方法调用或者特殊的异常被抛出。

*
   
建议(Advice):AOP框架在一个特殊连接点上采取的动作。这些不同类型的建议包括“around”、“before”和“throws”
等建议。很多AOP框架,包括Spring.NET,都把一个建议模拟成一个监听器,同时维护一个“around”连接点的监听器链。

*
    切点(Pointcut):一组连接点,用于指定建议应该激活的时间。一个AOP框架必须能够允许开发人员指定切点,例如,使用正则表达式。

*
    介绍(Introduction):添加方法或域到建议类。Spring.NET允许介绍一个新的接口到任何一个建议对象中。例如,为了简化对对象状态变化的跟踪,可以使用建议为任何对象实现一个IAuditable接口。

*
    目标对象:包含连接点的对象。

*
    AOP代理:由AOP框架创建的对象,包括建议。在Spring.NET中,一个AOP代理是一个在运行期使用IL代码生成的动态代理。

*
    Weaving:装配对象创建一个被建议对象。装配工作发生在编译期(例如使用Gripper-Loom .NET编译器),也能发生在运行期。Spring.NET在运行期执行装配动作。

4.Spring.NET应用实例

下面以经典的Movie Finder作为Spring.NET应用实例来讲解IoC容器的使用方法。实例的C#代码可以在Spring.NET发布版的examples/Spring/Spring.Examples.MovieFinder目录中找到。

(1)Movie Finder。MovieFinder例子的起始类是MovieApp类,这是具有单一应用程序入口点的普通.NET类。代码如下所示:

#001 using System;

#002 namespace Spring.Examples.MovieFinder

#003 {

#004     public class MovieApp

#005     {

#006          public static void Main ()

#007          {

#008          }

#009     }

#010 }

现在想做的是获得一个对MovieFinder类实例的引用。这是Spring.NET例子,所以要从Spring.NET的IoC容器类IApplicationContext获得这个引用。应用程序配置文件中的IApplicationContext配置信息如下:

#001 <?xml version="1.0" encoding="utf-8" ?>

#002 <configuration>

#003 <configSections>

#004 <sectionGroup name="spring">

#005 <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>

#006 <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />

#007 </sectionGroup>

#008 </configSections>

#009 <spring>

#010 <context>

#011 <resource uri="config://spring/objects"/>

#012 </context>

#013 <objects>

#014 <description>An example that demonstrates simple IoC features.</description>

#015 </objects>

#016 </spring>

#017 </configuration>

将在应用程序示例中用到的对象配置成嵌套在<objects/>元素中的<object/>元素。

(2)获得IApplicationContext应用。代码如下所示:

#001 using System;

#002 using System.Configuration;

#003 using Spring.Context;

#004 ...

#005 public static void Main ()

#006 { IApplicationContext ctx = ContextRegistry.GetContext();}

#007 ...

如上所述,using System.Configuratoin和using
Spring.Context两条using语句被加到了MovieApp类文件中。其中,System.Configuration命名空间可以让应用
程序存取保存在配置文件中的Spring.NET的IoC容器的定义;Spring.Context命名空间可以让应用程序存取
IApplicationContext类,这个类是应用程序存取Spring.NET所提供的功能的主要方法。

方法main获得一个IApplicationContext的实现,它的配置信息已经被写在应用程序配置文件内名为<objects/>的节中。

(3)第一个对象的定义。到目前为止,在应用程序配置文件中还没有对象定义,所以下面定义一个对象。MovieLister实例的XML定义如下所示。

#001 ...

#002 <objects>

#003    <object name="MyMovieLister"

#004           type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">

#005    </object>

#006 </object>

#007 ...

#003行:MyMovieLister是该对象的唯一标识符,使用这个标识符,这个对象的实例就能够被如下所示的代码中的IApplicationContext引用所获取。

#001 ...

#002 public static void Main ()

#003    { IApplicationContext ctx = ContextRegistry.GetContext();

#004      MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");}

#005 ...

lister实例仍然没有与IMovieFinder接口相应的实现注入。此时如果使用
MoviesDirectedBy方法会导致NullReferenceException异常发生,因为lister实例仍然还没有对
IMovieFinder的引用。注入到lister实例中的IMovieFinder接口实现的XML配置定义如下。

#001 ...

#002     <objects>

#003        ...

#004        <object name="MyMovieFinder"

#005       type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>

#006        </object>

#007         ...

#008     </object>

#009 ...

(4)设值注入。下面要做的是把以MyMovieFinder为标识符的IMovieFinder实例注入到以MyMovieLister为标识符的MovieLister实例中。这个注入动作使用设值注入的方法,代码如下所示:

#001 ...

#002 <objects>

#003 <object name="MyMovieLister"

#004 type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">

#005 <!-- using setter injection... -->

#006 <property name="movieFinder" ref="MyMovieFinder"/>

#007 </object>

#008 <object name="MyMovieFinder"

#009 type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>

#010 </object>

#011 </objects>

#012 ...

当IApplicationContext接口取得了MyMovieLister对象以后,
Spring.NET的IoC容器将会把对MyMovieLister对象的引用注入到MyMovieLister对象的MovieFinder属性中
去。这样,在程序中引用的MovieFinder对象即配置完毕,可以列出某导演导过的所有电影。

#001 ...

#002 public static void Main ()

#003 {

#004 IApplicationContext ctx = ContextRegistry.GetContext();

#005 MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");

#006 Movie[] movies = lister.MoviesDirectedBy("Roberto Benigni");

#007 Console.WriteLine ("/nSearching for movie.../n");

#008 foreach (Movie movie in movies)

#009 {Console.WriteLine (

#010 string.Format ("Movie Title = '{0}', Director = '{1}'.",

#011 movie.Title, movie.Director));}

#012 Console.WriteLine ("/nMovieApp Done./n/n");

#013 }

#014 ...

也可以使用构造注入的方法,读者可以自己考虑构造注入方法的实现方式,这里就不再具体介绍。

现在Spring.Net的1.0.2版本已经发布,读者可以通过访问http://sourceforge.net/ projects/ springnet下载。

抱歉!评论已关闭.