• Nhibernate学习心得


    1.注意Nhibernate的版本号
     

    NHibernate2.1版本改变了ByteCode延迟加载机制,有三种3种IoC框架动态代理方式,分别为:Castle框架、LinFu框架、Spring.Net框架。我们只要选择一种,在配置文件中配置proxyfactory.factory_class节点。

    如果使用Castle.DynamicProxy2动态代理,引用NHibernate.ByteCode.Castle.dll程序集并配置proxyfactory.factory_class节点为<property name="proxyfactory.factory_class"> NHibernate.ByteCode.Castle.ProxyFactoryFactory,NHibernate.ByteCode.Castle</property>

    如果使用LinFu.DynamicProxy动态代理,引用NHibernate.ByteCode.LinFu.dll程序集并配置proxyfactory.factory_class节点为<property name="proxyfactory.factory_class"> NHibernate.ByteCode.LinFu.ProxyFactoryFactory,NHibernate.ByteCode.LinFu</property>

    如果使用Spring.Aop动态代理,引用NHibernate.ByteCode.Spring.dll程序集并配置proxyfactory.factory_class节点为<property name="proxyfactory.factory_class"> NHibernate.ByteCode.Spring.ProxyFactoryFactory,NHibernate.ByteCode.Spring</property>

    另外NHibernate2.1要求.NET2.0 SP1以上版本 (System.DateTimeOffset),请使用VS2005的,务必打上Sp1补丁。推荐使用VS2008以上版本。

    我们要为Microsoft Visual Studio 2008添加编写NHibernate配置文件智能提示的功能。只要在下载的NHibernate里找到configuration.xsd和nhibernate-mapping.xsd两个文件并复制到X:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas目录即可。

    我们现在可以开始NHibernate了。首先,我们要从ISessionFactory中获取一个ISession(NHibernate的工作单元)。ISessionFactory可以创建并打开新的Session。一个Session代表一个单线程的单元操作。 ISessionFactory是线程安全的,很多线程可以同时访问它。ISession不是线程安全的,它代表与数据库之间的一次操作。ISession通过ISessionFactory打开,在所有的工作完成后,需要关闭。 ISessionFactory通常是个线程安全的全局对象,只需要被实例化一次。我们可以使用GoF23中的单例(Singleton)模式在程序中创建ISessionFactory。这个实例我编写了一个辅助类NHibernateHelper 用于创建ISessionFactory并配置ISessionFactory和打开一个新的Session单线程的方法,之后在每个数据操作类可以使用这个辅助类创建ISession 。

    public class NHibernateHelper
    {
        private ISessionFactory _sessionFactory;
        public NHibernateHelper()
        {
            _sessionFactory = GetSessionFactory();
        }
        private ISessionFactory GetSessionFactory()
        {
            return (new Configuration()).Configure().BuildSessionFactory();
        }
        public ISession GetSession()
        {
            return _sessionFactory.OpenSession();
        }
    }

     NHibernate查询语言(HQL,NHibernate Query Language)、

    条件查询(Criteria API,Query By Example(QBE)是Criteria API的一种特殊情况)、

    原生SQL(Literal SQL,T-SQL、PL/SQL)。

    1.HQL

    实例1:按照FirstName查询顾客:

    public IList<Customer> GetCustomersByFirstname(string firstname)
    {
        //写法1
        //return _session.CreateQuery("from Customer c where c.Firstname='" + firstname + "'")
        //    .List<Customer>();
    
        //写法2:位置型参数
        //return _session.CreateQuery("from Customer c where c.Firstname=?")
        //    .SetString(0, firstname)
        //    .List<Customer>();
    
        //写法3:命名型参数(推荐)
        return _session.CreateQuery("from Customer c where c.Firstname=:fn")
            .SetString("fn", firstname)
            .List<Customer>();
    }

    书写HQL参数有四种写法:

    • 写法1:可能会引起SQL注入,不要使用。
    • 写法2:ADO.NET风格的?参数,NHibernate的参数从0开始计数。
    • 写法3:命名参数用:name的形式在查询字符串中表示,这时IQuery接口把实际参数绑定到命名参数。
    • 写法4:命名的参数列表,把一些参数添加到一个集合列表中的形式,比如可以查询数据是否在这个集合列表中。

    使用命名参数有一些好处:命名参数不依赖于它们在查询字符串中出现的顺序;在同一个查询中可以使用多次;它们的可读性好。所以在书写HQL使用参数的时候推荐命名型参数形式。

    2.Criteria Query

    .结果集限制

    使用ICriteria接口提供的Add方法添加Restrictions类中约束表达式可以限制一些结果集的作用。

    public IList<Customer> Narrowing()
    {
        IList<Customer> customers = _session.CreateCriteria(typeof(Customer))
            .Add(Restrictions.Like("Firstname", "YJing%"))
            .Add(Restrictions.Between("Lastname", "A%", "Y%"))
            .List<Customer>();
        return customers;
    }

    排序

    使用ICriteria.Order对结果集排序,第二个参数true代表asc,false代表desc。例如下面例子查询Customer对象按FirstName降序、Lastname升序。

    public IList<Customer> Order()
    {
        return _session.CreateCriteria(typeof(Customer))
            .Add(Restrictions.Like("Firstname","Y%"))
            .AddOrder(new NHibernate.Criterion.Order("Firstname", false))
            .AddOrder(new NHibernate.Criterion.Order("Lastname", true))
            .List<Customer>();
    }
     
     
    5. 对象/关系数据库映射基础(Basic O/R Mapping)

    5. 对象/关系数据库映射基础(Basic O/R Mapping)

     

    5.1. 映射定义(Mapping declaration)

     

    对象和关系数据库之间的映射是用一个XML文档(XML document)来定义的。这个映射文档被设计为易读的,并且可以手工修改。映射语言是以对象为中心的, 意味着映射是按照持久化类的定义来创建的,而非表的定义。

    请注意,虽然很多Hibernate用户选择手工定义XML映射文档,也有一些工具来生成映射文档,包括NHibernate.Mapping.Attributes库,和各种基于模板的代码生成工具(CodeSmith, MyGeneration).

    让我们从一个映射的例子开始:

    <?xml version="1.0"?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Eg"
        namespace="Eg">
    
            <class name="Cat" table="CATS" discriminator-value="C">
                    <id name="Id" column="uid" type="Int64">
                            <generator class="hilo"/>
                    </id>
                    <discriminator column="subclass" type="Char"/>
                    <property name="BirthDate" type="Date"/>
                    <property name="Color" not-null="true"/>
                    <property name="Sex" not-null="true" update="false"/>
                    <property name="Weight"/>
                    <many-to-one name="Mate" column="mate_id"/>
                    <set name="Kittens">
                            <key column="mother_id"/>
                            <one-to-many class="Cat"/>
                    </set>
                    <subclass name="DomesticCat" discriminator-value="D">
                            <property name="Name" type="String"/>
                    </subclass>
            </class>
    
            <class name="Dog">
                    <!-- mapping for Dog could go here -->
            </class>
    
    </hibernate-mapping>
            

    我们现在开始讨论映射文档的内容。我们只描述NHibernate在运行时用到的文档元素和属性。映射文档还包括一些额外的可选属性和元素,它们在使用schema导出工具的时候会影响导出的数据库schema结果。(比如, not-null 属性。)

    5.1.1. XML名称空间

     

    所有的XML映射都需要定义XML名称空间。XSD文件可以NHibernate的分发包src\nhibernate-mapping.xsd里找到。

    小窍门: 为了开启编辑映射文件和配置文件的智能提示功能,请将上述.xsd文件拷贝到 <VS 2005 安装目录>\Xml\Schemas中去。

    5.1.2. hibernate-mapping

     

    这个元素包括以下可选的属性。schema属性,指明了这个映射所引用的表所在的schema名称。假若指定了这个属性, 表名会加上所指定的schema的名字扩展为全限定名。假若没有指定,表名就不会使用全限定名。default-cascade 指定了未明确注明cascade属性的.Net属性和集合类.Net会采取什么样的默认级联风格。 auto-import属性默认让我们在查询语言中可以使用非全限定名的类名。assembly namespace指定了持久化类的应用程序集名称和其所在的名称空间名。

    <hibernate-mapping                                   
    (1)
             schema="schemaName"                          (2)
             default-cascade="none|save-update"           (3)
             auto-import="true|false"                     (4)
             assembly="Eg"                                (5)
             namespace="Eg"
     />
    (1)

    schema (optional): 数据库schema名称。

    (2)

    default-cascade (可选 - 默认为 none): 默认的级联风格。

    (3)

    auto-import (optional - defaults to true): 指定我们在使用查询语句的时候是否可以使用非全限定名。

    (4)(5)

    assembly and namespace(可选): 指定映射文件中的类的应用程序集名称和其所在的名称空间名,用来生成类的非全限定名。

    如果没有设置assemblynamespace标签,我们不得不使用类的非全限定名 (namespace.类名,assembly).

    假若你有两个持久化类,它们的非全限定名是一样的,你应该设置auto-import="false"。 假若说你把一个“import过”的名字同时对应两个类, NHibernate会抛出一个异常。

    5.1.3. class

     

    你可以使用class元素来定义一个持久化类:

    <class                                               
    (1)
            name="ClassName"                              (2)
            table="tableName"                             (3)
            discriminator-value="discriminator_value"     (4)
            mutable="true|false"                          (5)
            schema="owner"                                (6)
            proxy="ProxyInterface"                        (7)
            dynamic-update="true|false"                   (8)
            dynamic-insert="true|false"                   (9)
            select-before-update="true|false"             (10)
            polymorphism="implicit|explicit"              (11)
            where="arbitrary sql where condition"         (12)
            persister="PersisterClass"                    (13)
            batch-size="N"                                (14)
            optimistic-lock="none|version|dirty|all"      (15)
            lazy="true|false"                             (16)
            abstract="true|false"
    />
    (1)

    name: 持久化类(或者接口)的.NET全限定名。

    (2)

    table: 对应的数据库表名。

    (3)

    discriminator-value(辨别值) (可选 - 默认和类名一样):一个用于区分不同的子类的值,在多态行为时使用。可选值包括nullnot null

    (4)

    mutable (可选, 默认值为 true): 表明该类的实例可变(不可变)。

    (5)

    schema (可选) 覆盖在根元素中指定的schema名字。

    (6)

    proxy (可选)指定一个接口,在延迟装载时作为代理使用。你可以在这里使用该类自己的名字。

    (7)

    dynamic-update (可选,默认为false): 指定用于UPDATE 的SQL将会在运行时动态生成,并且只更新那些改变过的字段。

    (8)

    dynamic-insert (可选, 默认为false): 指定用于 INSERT的 SQL 将会在运行时动态生成,并且只包含那些非空值字段。

    (9)

    select-before-update (可选,默认值为 false): 指定NHibernate除非确定对象的确被修改了,UPDATE操作。在特定场合(实际上,只会发生在一个临时对象关联到一个新的session中去,执行update()的时候),这说明NHibernate会在UPDATE之前执行一次额外的SQL SELECT操作,来决定是否应该进行UPDATE。

    (10)

    polymorphism (可选, 默认值为 implicit (隐式)): 界定是隐式还是显式的使用查询多态。

    (11)

    where (可选) 指定一个附加的SQL WHERE 条件,在抓取这个类的对象时会一直增加这个条件。

    (12)

    persister (可选): 指定一个定制的 IClassPersister

    (13)

    batch-size (可选,默认是1) 指定一个用于根据标识符抓取实例时使用的"batch size"(批次抓取数量)。

    (14)

    optimistic-lock (乐观锁定) (可选,默认是version): 决定乐观锁定的策略。

    (15)

    lazy (可选): 假若设置lazy="false",就会禁用延迟加载。

    (16)

    abstract(可选) 用于在<union-subclass>的继承结构 (hierarchies)中标识抽象超类。

    若指明的持久化类实际上是一个接口,这也是完全可以接受的。 之后你可以用元素 <subclass>来指定该接口的实际实现类。 你可以持久化任何static(静态的)内部类。 你应该使用标准的类名格式来指定类名,比如:Eg.Foo+Bar, Eg。由于HQL解析器的限制NHibernate 1.0 无法在查询里使用内部类。

    不可变类 mutable="false"不可以被应用程序更新或者删除。 这可以让NHibernate做一些小小的性能优化。

    可选的proxy属性允许延迟加载类的持久化实例。 NHibernate开始会返回实现了这个命名接口的代理类。当代理的某个方法被实际调用的时候, 真实的持久化对象才会被装载。参见下面的“用于延迟装载的代理”。

    Implicit (隐式)的多态是指,如果查询时给出的是任何超类、该类实现的接口或者该类的 名字,都会返回这个类的实例;如果查询中给出的是子类的名字,则会返回子类的实例。 Explicit (显式)的多态是指,只有在查询时给出明确的该类名字时才会返回这个类的实例; 同时只有在这个<class>的定义中作为<subclass>或者<joined-subclass>出现的子类,才会可能返回。 在大多数情况下,默认的polymorphism="implicit"都是合适的。 显式的多态在有两个不同的类映射到同一个表的时候很有用。(允许一个“轻型”的类,只包含部分表字段)。

    persister属性可以让你定制这个类使用的持久化策略。 你可以指定你自己实现 NHibernate.Persister.EntityPersister的子类,你甚至可以完全从头开始编写一个 NHibernate.Persister.IClassPersister接口的实现, 比如是用储存过程调用、序列化到文件或者LDAP数据库来实现。 参阅NHibernate.DomainModel.CustomPersister,这是一个简单的例子 (“持久化”Hashtable)。

    请注意dynamic-updatee和dynamic-insert的设置并不会继承到子类, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次设置。这些设置是否能够提高效率要视情形而定。请用你的智慧决定是否使用。

    使用select-before-update通常会降低性能。如果你重新连接一个脱管(detache)对象实例 到一个Session中时,它可以防止数据库不必要的触发update。 这就很有用了。

    如果你打开了dynamic-update,你可以选择几种乐观锁定的策略:

    • version(版本检查) 检查version/timestamp字段

    • all(全部) 检查全部字段

    • dirty(脏检查)只检察修改过的字段

    • none(不检查)不使用乐观锁定

    我们非常强烈建议你在NHibernate中使用version/timestamp字段来进行乐观锁定。 对性能来说,这是最好的选择,并且这也是唯一能够处理在session外进行操作的策略(例如: 在使用ISession.Update()的时候)。注意version或者是timestamp属性不能为null,不管是否使用了unsaved-value策略,或者是实例被作为是瞬态。

    从NHibernate 1.2.0开始,版本号从1开始(以前的版本从0开始),这样允许把version的属性的unsaved-value设置为0。

    5.1.4. id

     

    被映射的类必须定义对应数据库表主键字段。大多数类有一个属性, 为每一个实例包含唯一的标识。 元素定义了该属性到数据库表主键字段的映射。

    <id                                              (1)
            name="PropertyName"                      (2)
            type="typename"                          (3)
            column="column_name"                     (4)
            unsaved-value="any|none|null|id_value"   (5)
            access="field|property|nosetter|ClassName">
    
            <generator class="generatorClass"/>
    </id>
    (1)

    name (可选): 标识属性的名字。

    (2)

    type(可选): 标识NHibernate类型的名字。

    (3)

    column(可选 - 默认为属性名): 主键字段的名字。

    (4)

    unsaved-value (可选 - 默认为一个切合实际(sensible)的值): 一个特定的标识属性值,用来标志该实例是刚刚创建的,尚未保存。 这可以把这种实例和从以前的session中装载过(可能又做过修改--译者注) 但未再次持久化的实例区分开来。

    (5)

    access (可选 - 默认为property): NHibernate用来访问属性值的策略。

    如果name属性不存在,会认为这个类没有标识属性。

    unsaved-value属性在NHibernate 1.0中几乎不再需要。

    还有一个另外的<composite-id>定义可以访问旧式的多主键数据。 我们强烈不建议使用这种方式。

    5.1.4.1. (主键生成策略)generator

     

    可选的<generator>子元素是一个.NET类的名字, 用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数, 用

    <id name="Id" type="Int64" column="uid" unsaved-value="0">
            <generator class="NHibernate.Id.TableHiLoGenerator">
                    <param name="table">uid_table</param>
                    <param name="column">next_hi_value_column</param>
            </generator>
    </id>

    所有的生成器都实现NHibernate.Id.IIdentifierGenerator接口。 这是一个非常简单的接口; 某些应用程序可以选择提供他们自己特定的实现。当然, NHibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字:

    increment

    用于为int类型生成 唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用

    identity

    对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。数据库返回的主键值 返回的标识符是int类型的。

    sequence

    在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的标识符是int类型的。

    hilo

    使用一个高/低位算法来高效的生成int类型的标识符。给定一个表和字段(默认分别是是hibernate_unique_key 和next_hi)作为高位值得来源。 高/低位算法生成的标识符只在一个特定的数据库中是唯一的。在用户自行提供的连接中,不要使用这种生成器。

    你可以使用where参数来指定表里面用了多少列数据, You can use the "where" parameter to specify the row to use in a table. This is useful if you want to use a single tabel for your identifiers, with different rows for each table.

    seqhilo

    使用一个高/低位算法来高效的生成int类型的标识符,给定一个数据库序列(sequence)的名字。

    uuid.hex

    用一个System.Guid的ToString()方法法生成字符串类型的标识符, 字符串的长度由format参数定义。

    uuid.string

    用一个新的System.Guid实例的byte[]转化为字符串作为标示符。

    guid

    使用新的System.Guid实例作为标示符。

    guid.comb

    使用Jimmy Nilsson的算法(请参阅http://www.informit.com/articles/article.asp?p=25862)生成一个新的System.Guid标示符。

    native

    根据底层数据库的能力选择identity, sequence 或者hilo中的一个。

    assigned

    让应用程序在 Save()之前为对象分配一个标示符。

    foreign

    使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。

    5.1.4.2. 高/低位算法(Hi/Lo Algorithm)

     

    hilo 和 seqhilo生成器给出了两种hi/lo算法的实现, 这是一种很令人满意的标识符生成算法。 第一种实现需要一个“特殊”的数据库表来保存下一个可用的“hi”值。 第二种实现使用一个Oracle风格的序列(在被支持的情况下)。

    <id name="Id" type="Int64" column="cat_id">
            <generator class="hilo">
                    <param name="table">hi_value</param>
                    <param name="column">next_value</param>
                    <param name="max_lo">100</param>
            </generator>
    </id>
    <id name="Id" type="Int64" column="cat_id">
            <generator class="seqhilo">
                    <param name="sequence">hi_value</param>
                    <param name="max_lo">100</param>
            </generator>
    </id>

    很不幸,你在为NHibernate自行提供IDbConnection时无法使用hilo。 NHibernate必须能够在一个事务里获取"hi"值。

    5.1.4.3. UUID算法(UUID Algorithm )

     
    <id name="Id" type="String" column="cat_id">
            <generator class="uuid.hex">
                <param name="format">format_value</param>
                <param name="seperator">seperator_value</param>
            </generator>
    </id>

    UUID算法是调用Guid.NewGuid().ToString(format)方法生成标示符。 format参数的使用请参阅MSDN,GUID的默认分隔符是-, 这个基本不会改动。format可以决定是都替换默认的默认分隔符。 The UUID is generated by calling Guid.NewGuid().ToString(format)

    5.1.4.4. UUID字符串算法

     

    这个算法调用Guid.NewGuid().ToByteArray()方法获取byte[], 然后将byte[]转换为char[]char[]被作为一个长度 为16的字符串返回。

    5.1.4.5. GUID 算法

     

    guid标示符通过调用 Guid.NewGuid()创建。当在MSSQL中使用Guids标示符时做主键,外键或者是索引是为了获取更好的性能 通常使用 guid.comb。其他支持GUIDs标示的的数据库使用guid.comb能否获得性能的提升未知。

    5.1.4.6. 标识字段和序列(Identity columns and Sequences)

     

    对于内部支持标识字段的数据库(DB2,MySQL,Sybase,MS SQL),你可以使用identity关键字生成。 对于内部支持序列的数据库(DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB), 你可以使用sequence风格的关键字生成。 这两种方式对于插入一个新的对象都需要两次SQL查询。

    <id name="Id" type="Int64" column="uid">
            <generator class="sequence">
                    <param name="sequence">uid_sequence</param>
            </generator>
    </id>
    <id name="Id" type="Int64" column="uid" unsaved-value="0">
            <generator class="identity"/>
    </id>

    对于跨平台开发,native策略会从identity, sequence 和hilo中进行选择,选择哪一个,这取决于底层数据库的支持能力。

    5.1.4.7. 程序分配的标识符(Assigned Identifiers)

     

    如果你需要应用程序分配一个标示符(而非NHibernate来生成它们),你可以使用assigned生成器。 这种特殊的生成器会使用已经分配给对象的标识符属性的标识符值。用这种特性来分配商业行为的关键字要特别小心(基本上总是一种可怕的设计决定)。

    因为其继承天性,使用这种生成器策略的实体不能通过ISession的SaveOrUpdate()方法保存。作为替代, 你应该明确告知NHibernate是应该被save还是update,分别调用ISession的Save()或Update()方法。

    5.1.5. composite-id 联合ID

     
    <composite-id
            name="PropertyName"
            class="ClassName"
            unsaved-value="any|none"
            access="field|property|nosetter|ClassName">
    
            <key-property name="PropertyName" type="typename" column="column_name"/>
            <key-many-to-one name="PropertyName class="ClassName" column="column_name"/>
            ......
    </composite-id>

    如果表使用联合主键,你可以把类的多个属性组合成为标识符属性。<composite-id> 元素接受<key-property>属性映射和 <key-many-to-one>属性映射作为子元素。

    <composite-id>
            <key-property name="MedicareNumber"/>
            <key-property name="Dependent"/>
    </composite-id>

    你的持久化类必须重载 Equals()GetHashCode()方法,来实现组合的标识符判断等价.也必须实现可序列化

    不幸的是,这种组合关键字的方法意味着一个持久化类是它自己的标识。除了对象自己之外, 没有什么方便的“把手”可用。你必须自己初始化持久化类的实例,在使用组合关键字load()持久化状态之前, 必须填充他的联合属性。我们会在 7.4. 组件作为联合标识符(Components as composite identifiers)第 7.4 节 “组件作为联合标识符(Components as composite identifiers)”章中说明一种更加方便的方法, 把联合标识实现为一个独立的类,下面描述的属性只对这种备用方法有效:

    • name (可选):一个组件类型,持有联合标识(参见下一节)。

    • access (可选 - 默认为property): NHibernate应该使用的访问此属性值的策略

    • class (可选 - 默认为通过反射(reflection)得到的属性类型) : 作为联合标识的组件类名(参见下一节)。

    5.1.6. 鉴别器

     

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

    <discriminator
            column="discriminator_column" 
    (1)
            type="discriminator_type"      (2)
            force="true|false"             (3)
            insert="true|false"            (4)
            formula="arbitrary SQL expressi(5)on"
    />
    (1)

    column (可选 - 默认为 class) 鉴别器字段的名字

    (2)

    type (可选 - 默认为 String) 一个NHibernate字段类型的名字。

    (3)

    force(强制) (可选 - 默认为 false) "强制"NHibernate指定允许的鉴别器值,即使当取得的所有实例都是根类的。

    (4)

    insert (可选 - 默认为true) 如果你的鉴别器字段也是映射为复合标识(composite identifier)的一部分, 则需将 这个值设为false。

    (5)

    formula (可选) 一个SQL表达式,在类型判断(判断是父类还是具体子类-译注)时执行。可用于基于内容的鉴别器。

    鉴别器字段的实际值是根据discriminator-value<class> and <subclass>元素中 的discriminator-value属性得来的。

    force属性仅仅在这种情况下有用的:表中包含没有被映射到持久化类的附加辨别器值。 这种情况不会经常遇到。

    使用formula属性你可以定义一个SQL表达式,用来判断一个行数据的类型。

    <discriminator
        formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
        type="Int32"/>

    5.1.7. 版本(version)(可选)

     

    <version>元素是可选的,表明表中包含附带版本信息的数据。 这在你准备使用 长事务(long transactions)的时候特别有用。(见后)

    <version
            column="version_column"                           
    (1)
            name="PropertyName"                                (2)
            type="typename"                                    (3)
            access="field|property|nosetter|ClassName"         (4)
            unsaved-value="null|negative|undefined|value"      (5)
            generated="never|always"                           (6)
    />
    (1)

    column (可选 - 默认为属性名): 指定持有版本号的字段名。

    (2)

    name 持久化类的属性名。

    (3)

    type (可选 - 默认是 Int32): 版本号的类型。

    (4)

    access (可选 - 默认是 property): NHibernate用于访问属性值的策略。

    (5)

    unsaved-value(可选 - 默认是undefined): 用于标明某个实例时刚刚被实例化的(尚未保存)版本属性值, 依靠这个值就可以把这种情况 和已经在先前的session中保存或装载的脱管(detached)实例区分开来。 (undefined指明应被使用的标识属性值。)

    (6)

    generated(可选 - 默认是 never): 表明此版本属性值是否实际上是由数据库生成的。 请参阅 5.5  “数据库生成属性(Generated Properties)”部分的讨论。

    版本号必须是以下类型:Int64, Int32, Int16, Ticks,或者 Timestamp, TimeSpan

    5.1.8. timestamp (可选)

     

    可选的<timestamp>元素指明了表中包含时间戳数据。 这用来作为版本的替代。 时间戳本质上是一种对乐观锁定的一种不是特别安全的实现。当然, 有时候应用程序可能在其他方面使用时间戳。

    <timestamp
            column="timestamp_column"          
    (1)
            name="PropertyName"                 (2)
            access="field|property|nosetter|Clas(3)sName"
            unsaved-value="null|undefined|value"(4)
            generated="never|always"            (5)
    />
    (1)

    column(可选 - 默认为属性名): 持有时间戳的字段名。

    (2)

    name: 在持久化类中的.NE风格的属性名, 其.NE类型是 DateTime的。

    (3)

    access (可选 - 默认是 property): NHibernate用于访问属性值的策略。

    (4)

    unsaved-value (可选 - 默认是null): 用于标明某个实例时刚刚被实例化的(尚未保存)版本属性值, 依靠这个值就可以把这种情况和 已经在先前的session中保存或装载的脱管(detached)实例区分开来。 (undefined 指明使用标识属性值进行这种判断。)

    (5)

    generated : 指出时间戳值是否实际上是由数据库生成的.请参阅5.5  “数据库生成属性(Generated Properties)”的讨论。

    注意<timestamp>等价于 <version type="timestamp">

    5.1.9. property

     

    <property>元素为类定义了一个持久化类的属性。

    <property
            name="propertyName"                
    (1)
            column="column_name"                (2)
            type="typename"                     (3)
            update="true|false"                 (4)
            insert="true|false"                 (4)
            formula="arbitrary SQL expression"  (5)
            access="field|property|ClassName"   (6)
            optimistic-lock="true|false"        (7)
            generated="never|insert|always"     (8)
    />
    (1)

    name: 属性的名字。

    (2)

    column (可选 - 默认为属性名字): 对应的数据库字段名。

    (3)

    type (可选): 一个NHibernate类型的名字。

    (4)

    update, insert (可选 - 默认为 true) : 表明用于UPDATE 和/或 INSERT 的SQL语句中是否包含这个被映射了的字段。 这二者如果都设置为false 则表明这是一个“外源性(derived)”的属性, 它的值来源于映射到同一个(或多个) 字段的某些其他属性,或者通过一个trigger(触发器)或其他程序生成。

    (5)

    formula (可选): 一个SQL表达式,定义了这个计算 (computed) 属性的值。计算属性没有和它对应的数据库字段。

    (6)

    access (可选 - 默认值为 property): NHibernate用来访问属性值的策略。

    (7)

    optimistic-lock (可选 - 默认为 true): 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。

    (8)

    generated (可选 - 默认为 never): 表明此属性值是否实际上是由数据库生成的。 请参阅5.5  “数据库生成属性(Generated Properties)”的讨论。

    typename可以是如下几种

    1. NHibernate基本类型名(比如:Int32, String, Char, DateTime, Timestamp, Single, Byte[], Object, ...)。

    2. 一个.Net类的名字,这个类属于一种默认基础类型 (比如: System.Int16, System.Single, System.Char, System.String, System.DateTime, System.Byte[], ...)。

    3. 一个枚举类型的名字。(比如:. eg.Color)。

    4. 一个可以序列化的.NET类的名字。

    5. 一个自定义类型的类的名字。(比如: Illflow.Type.MyCustomType)。

    注意你必须为所有类型(除了NHibernate基础类型)指定完整的应用程序集权限定名 (或者是在<hibernate-mapping>里面配置了assemblynamespace属性)。

    NHibernate支持.NET 2.0的可空类型,这些类型和对应的非可空类型处理方式是一致的, 例如:Nullable<Int32>可以对应type="Int32"或者是type="System.Int32"

    如果你没有指定类型,NHibernate会使用反射来得到这个名字的属性, 以此来猜测正确的NHibernate类型。NHibernate会对属性读取器(getter方法)的返回类进行解释, 按照规则2,3,4的顺序。然而,这并不足够。 在某些情况下你仍然需要type属性。 (比如,为了区别NHibernateUtil.DateTimeNHibernateUtil.Timestamp,或者为了指定一个自定义类型。)

    access属性用来让你控制NHibernate如何在运行时访问属性。 在默认情况下, NHibernate会按照access-strategy.naming-strategy来格式化属性名 .naming-strategy不是必填项。

    表 5.1. 访问策略

    访问策略名描述
    property

    默认实现:NHibernate访问类的set/get属性,这种方式没有命名策略,因为name就代表属性的名称。

    field

    NHibernate将会直接访问成员变量。NHibernate使用name作为成员变量的名称。 当对象属性的get和set里面有额外的操作,而你不想让NHibernate设置或者读取对象时执行额外的操作, 可以用这个策略。当你使用HQL时需要属性名而非字段时,就需要命名策略(Naming Strateg)。

    nosetter

    NHibernate将会在设置值时直接访问字段,获得值时访问属性。 当API使用者不能直接改变值,因而只为属性只提供了get访问器时, 你可以用这个策略。NHibernate使用name属性(attribute)作为属性(Property ), 并且需要提供字段名,所以命名策略必须(Naming Strategy)使用。

    ClassName

    如果NHibernate内置的访问策略(Access Strategie)不能满足你的要求。 你可以通过实现NHibernate.Property.IPropertyAccessor接口来自己的访问策略(Access Strategie)。 这个值需要用程序集名(Assembly)来限定,这样就能通过 Activator.CreateInstance(string AssemblyQualifiedName)来读取。

    表 5.2. 命名策略(Naming Strategy)

    命名策略描述
    camelcase

    name属性被转换CamelCase格式来查找字段。<property name="Foo" ... >使用foo字段。

    camelcase-underscore

    name属性被转换CamelCase格式并添加下划线前缀来查找字段。<property name="Foo" ... >使用_foo字段。

    lowercase

    name属性被转换小写格式来查找字段。<property name="FooBar" ... > 使用 foobar字段.

    lowercase-underscore

    name属性被转换小写格式并添加下划线前缀来查找字段。<property name="FooBar" ... >使用_foobar字段.

    pascalcase-underscore

    name属性添加下划线前缀来查找字段。<property name="Foo" ... >使用_Foo字段。

    pascalcase-m

    name属性添加字母m前缀来查找字段。<property name="Foo" ... >使用mFoo字段。

    pascalcase-m-underscore

    name属性添加字母m和下划线前缀来查找字段。<property name="Foo" ... > 使用m_Foo字段。

    5.1.10. 多对一(many-to-one)

     

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

    <many-to-one
            name="PropertyName"                               
    (1)
            column="column_name"                               (2)
            class="ClassName"                                  (3)
            cascade="all|none|save-update|delete"              (4)
            fetch="join|select"                                (5)
            update="true|false"                                (6)
            insert="true|false"                                (6)
            property-ref="PropertyNameFromAssociatedClass"     (7)
            access="field|property|nosetter|ClassName"         (8)
            unique="true|false"                                (9)
            optimistic-lock="true|false"                       (10)
            not-found="ignore|exception"                       (11)
    />
    (1)

    name:属性名。

    (2)

    column数据库字段名

    (3)

    class(可选 - 默认是通过反射得到属性类型): 关联的类的名字。

    (4)

    cascade(级联) (可选): 指明哪些操作会从父对象级联到关联的对象。

    (5)

    fetch (可选 - 默认为 select): 在外连接抓取(outer-join fetching)和序列选择抓取(sequential select fetching)两者中选择其一。

    (6)

    (可选 - defaults to true) 指定对应的字段是否包含在用于UPDATE 和/或 INSERT 的SQL语句中。如果二者都是false,则这是一个纯粹的 “外源性(derived)”关联,它的值是通过映射到同一个(或多个)字段的某些其他属性得到 或者通过trigger(触发器)、或其他程序。

    (7)

    property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。 如果没有指定,会使用对方关联类的主键。

    (8)

    access(可选 - 默认是 property): NHibernate用来访问属性的策略。

    (9)

    unique (可选): 使用DDL为外键字段生成一个唯一约束。此外, 这也可以用作property-ref的目标属性。这使关联同时具有 一对一的效果。

    (10)

    optimistic-lock (可选 - 默认为 true): 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。

    (11)

    not-found (可选 - 默认为 exception): 指定外键引用的数据不存在时如何处理: ignore会将数据不存在作为关联到一个空对象(null)处理。

    cascade属性允许下列值:: all, save-update, delete, none. 设置除了none以外的其它值会传播特定的操作到关联的(子)对象中。参见后面的“Lifecycle Objects(自动管理生命周期的对象)”。

    fetch参数允许下列两个不同值:

    • join外连接抓取

    • select使用隔离查询抓取

    一个典型的简单many-to-one 定义例子:

    <many-to-one name="product" class="Product" column="PRODUCT_ID"/>

    property-ref属性只应该用来对付老旧的数据库系统, 可能有外键指向对方关联表的是个非主键字段(但是应该是一个惟一关键字)的情况下。 这是一种十分丑陋的关系模型。比如说,假设Product类有一个惟一的序列号, 它并不是主键。(unique属性控制NHibernate通过SchemaExport工具生成DDL的过程。)

    <property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

    那么关于OrderItem 的映射可能是:

    <many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

    当然,我们决不鼓励这种用法。

    5.1.11. 一对一

     

    持久化对象之间一对一的关联关系是通过 one-to-one元素定义的。

    <one-to-one
            name="PropertyName"                               
    (1)
            class="ClassName"                                  (2)
            cascade="all|none|save-update|delete"              (3)
            constrained="true|false"                           (4)
            fetch="join|select"                                (5)
            property-ref="PropertyNameFromAssociatedClass"     (6)
            access="field|property|nosetter|ClassName"         (7)
    />
    (1)

    name: 属性的名字。

    (2)

    class (可选 - 默认是通过反射得到的属性类型):被关联的类的名字。

    (3)

    cascade(级联) (可选) 表明操作是否从父对象级联到被关联的对象。

    (4)

    constrained(约束) (可选) 表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。 这个选项影响Save()和Delete()在级联执行时的先后顺序以及 决定该关联能否被委托(也在schema export tool中被使用).

    (5)

    fetch (可选 - 默认设置为select): 在外连接抓取或者序列选择抓取选择其一.

    (6)

    property-ref: (可选) 指定关联类的属性名,这个属性将会和本类的主键相对应。如果没有指定,会使用对方关联类的主键。

    (7)

    access (可选 - 默认是 property): NHibernate用来访问属性的策略。

    有两种不同的一对一关联:

    • 主键关联

    • 惟一外键关联

    主键关联不需要额外的表字段;如果两行是通过这种一对一关系相关联的,那么这两行就共享同样的主关键字值。所以如果你希望两个对象通过主键一对一关联,你必须确认它们被赋予同样的标识值!

    比如说,对下面的Employee和Person进行主键一对一关联:

    <one-to-one name="Person" class="Person"/>
    <one-to-one name="Employee" class="Employee" constrained="true"/>

    现在我们必须确保PERSON和EMPLOYEE中相关的字段是相等的。我们使用一个被成为foreign的特殊的现在我们必须确保PERSON和EMPLOYEE中相关的字段是相等的。我们使用一个被成为foreign的特殊的hibernate标识符生成策略:标识符生成策略: foreign:

    <class name="Person" table="PERSON">
        <id name="Id" column="PERSON_ID">
            <generator class="foreign">
                <param name="property">Employee</param>
            </generator>
        </id>
        ...
        <one-to-one name="Employee"
            class="Employee"
            constrained="true"/>
    </class>

    一个刚刚保存的Person实例被赋予和该Person的employee属性所指向的Employee实例同样的关键字值。

    另一种方式是一个外键和一个惟一关键字对应,上面的Employee和Person的例子,如果使用这种关联方式,可以表达成:

    <many-to-one name="Person" class="Person" column="PERSON_ID" unique="true"/>

    如果在Person的映射加入下面几句,这种关联就是双向的:

    <one-to-one name="Employee" class="Employee" property-ref="Person"/>

    5.1.12. 组件(component), 动态组件(dynamic-component)

     

    <component>元素把子对象的一些元素与父类对应的表的一些字段映射起来。 然后组件可以定义它们自己的属性、组件或者集合。参见后面的“Components”一章。

    <component 
            name="PropertyName"                               
    (1)
            class="ClassName"                                  (2)
            insert="true|false"                                (3)
            upate="true|false"                                 (4)
            access="field|property|nosetter|ClassName"         (5)
            optimistic-lock="true|false"                       (6)
    >
            
            <property ...../>
            <many-to-one .... />
            ........
    </component>
    (1)

    name: 属性名

    (2)

    class (可选 - 默认为通过反射得到的属性类型):组件(子)类的名字。

    (3)

    insert: 被映射的字段是否出现在SQL的INSERT语句中?

    (4)

    update: 被映射的字段是否出现在SQL的UPDATE语句中?

    (5)

    access可选 - 默认是 property): NHibernate用来访问属性的策略。

    (6)

    optimistic-lock (可选 - 默认是 true):表明更新此组件是否需要获取乐观锁。换句话说,当这个属性变脏时,是否增加版本号(Version

    <property>子标签为子类的一些属性与表字段之间建立映射。

    <component>元素允许加入一个 <parent>子元素,在组件类内部就可以有一个指向其容器的实体的反向引用。

    <dynamic-component>元素允许把一个 IDictionaryp映射为组件,其属性名对应键值。 参见第 8.5 节 “动态组件 (Dynamic components)”.

    5.1.13. 子类(subclass)

     

    最后,多态持久化需要为父类的每个子类都进行定义。 对于“每一棵类继承树对应一个表”的策略来说,就需要使用<subclass>定义。

    <subclass
            name="ClassName"                             
    (1)
            discriminator-value="discriminator_value"     (2)
            proxy="ProxyInterface"                        (3)
            lazy="true|false"                             (4)
            dynamic-update="true|false"
            dynamic-insert="true|false">
    
            <property .... />
            .....
    </subclass>
    (1)

    name: 子类的全限定名。

    (2)

    discriminator-value (辨别标志) (可选 - 默认为类名):一个用于区分每个独立的子类的值。

    (3)

    proxy(代理) (可选): 指定一个类或者接口,在延迟装载时作为代理使用。

    (4)

    lazy(可选, 默认是true): 设置为 lazy="false" 禁止使用延迟抓取。

    每个子类都应该定义它自己的持久化属性和子类。 <version><id>属性可以从根父类继承下来。 在一棵继承树上的每个子类都必须定义一个唯一的discriminator-value。如果没有指定,就会使用.NET类的全限定名。

    更多关于继承映射的信息, 参考 8. 继承映射章节.

    5.1.14. 连接的子类(joined-subclass)

     

    此外,每个子类可能被映射到他自己的表中(每个子类一个表的策略)。被继承的状态通过和超类的表关联得到。我们使用<joined-subclass>元素。

    <joined-subclass
            name="ClassName"                   
    (1)
            proxy="ProxyInterface"              (2)
            lazy="true|false"                   (3)
            dynamic-update="true|false"
            dynamic-insert="true|false">
    
            <key .... >
    
            <property .... />
            .....
    </joined-subclass>
    (1)

    name: 子类的全限定名。

    (2)

    proxy (可选): 指定一个类或者接口,在延迟装载时作为代理使用。

    (3)

    lazy(可选, 默认是 true): 设置为 lazy="false" 禁止使用延迟装载。 等价于设置proxy为自身类。

    这种映射策略不需要指定辨别标志(discriminator)字段。但是,每一个子类都必须使用 <key>元素指定一个表字段来持有对象的标识符。本章开始的映射可以被用如下方式重写:

    <?xml version="1.0"?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Eg"
        namespace="Eg">
    
            <class name="Cat" table="CATS">
                    <id name="Id" column="uid" type="Int64">
                            <generator class="hilo"/>
                    </id>
                    <property name="BirthDate" type="Date"/>
                    <property name="Color" not-null="true"/>
                    <property name="Sex" not-null="true"/>
                    <property name="Weight"/>
                    <many-to-one name="Mate"/>
                    <set name="Kittens">
                            <key column="MOTHER"/>
                            <one-to-many class="Cat"/>
                    </set>
                    <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
                        <key column="CAT"/>
                            <property name="Name" type="String"/>
                    </joined-subclass>
            </class>
    
            <class name="Dog">
                    <!-- mapping for Dog could go here -->
            </class>
    
    </hibernate-mapping>

    更多关于继承映射的信息,参考 9. 继承继承

    5.1.15. 联合子类(union-subclass)

     

    第三种选择是仅仅映射类继承树中具体类部分到表中(每个具体类一张表的策略)。 其中,每张表定义了类的所有持久化状态,包括继承的状态。 在 NHibernate 中,并不需要完全显式地映射这样的继承树。 你可以简单地使用单独的<class>定义映射每个类。 然而,如果你想使用多态关联(例如,一个对类继承树中超类的关联),你需要使用<union-subclass>映射。

    <union-subclass
            name="ClassName"                   
    (1)
            table="tablename"                   (2)
            proxy="ProxyInterface"              (3)
            lazy="true|false"                   (4)
            dynamic-update="true|false"
            dynamic-insert="true|false"
            schema="schema"
            catalog="catalog"
            extends="SuperclassName"
            abstract="true|false"
            persister="ClassName"
            subselect="SQL expression"
            entity-name="EntityName"
            node="element-name">
    
            <property .... />
            .....
    </union-subclass>
    (1)

    name: 子类的全限定名。

    (2)

    table: 子类的表名

    (3)

    proxy (可选): 指定一个类或者接口,在延迟装载时作为代理使用。

    (4)

    lazy (可选, 默认是 true): 设置为 lazy="false" 禁止使用延迟装载。

    这种映射策略不需要指定辨别标志(discriminator)字段。

    更多关于继承映射的信息,参考 9. 继承继承

    5.1.16. 连接(join)

     

    使用 <join> 元素,可以将一个类的属性映射到多张表中。 当表之间存在一对一关系的时候使用。

    <join
            table="tablename"                       
    (1)
            schema="owner"                           (2)
            fetch="join|select"                      (3)
            inverse="true|false"                     (4)
            optional="true|false">                   (5)
    
            <key ... />
    
            <property ... />
            ...
    </join>
    (1)

    table: 被连接表的名称。

    (2)

    schema (可选):覆盖由根<hibernate-mapping>元素指定的模式名称。

    (3)

    fetch (可选 - 默认是 join): 如果设置为默认值join, NHibernate 将使用一个内连接来得到这个类或其超类定义的join ,而使用一个外连接来得到其子类定义的join。如果设置为select, 则 NHibernate 将为子类定义的 join使用顺序选择。 这仅在一行数据表示一个子类的对象的时候才会发生。对这个类和其超类定义的join,依然会使用内连接得到。

    (4)

    inverse(可选 - 默认是 false): 如果打开,NHibernate 不会插入或者更新此连接定义的属性。

    (5)

    optional (可选 - 默认是 false): 如果打开,NHibernate 只会在此连接定义的属性非空时插入一行数据,并且总是使用一个外连接来得到这些属性。

    例如,一个人(person)的地址(address)信息可以被映射到单独的表中(并保留所有属性的值类型语义):

    <class name="Person"
        table="PERSON">
    
        <id name="id" column="PERSON_ID">...</id>
    
        <join table="ADDRESS">
            <key column="ADDRESS_ID"/>
            <property name="address"/>
            <property name="zip"/>
            <property name="country"/>
        </join>
        ...

    此特性常常对遗留数据模型有用,我们推荐表个数比类个数少,以及细粒度的领域模型。然而,在单独的继承树上切换继承映射策略是有用的,后面会解释这点。

    5.1.17. map, set, list, bag

     

    集合类型稍后讨论。

    5.1.18. 引用

     

    假设你的应用程序有两个同样名字的持久化类,但是你不想在Hibernate查询中使用他们的全限定名。 除了依赖auto-import="true"以外,类也可以被显式地“import(引用)”。你甚至可以引用没有明确被映射的类和接口。

    <import class="System.Object" rename="Universe"/>
    <import
            class="ClassName"              (1)
            rename="ShortName"             (2)
    />
    (1)

    class: 任何.NET类的全限定名。包括应用程序集名

    (2)

    rename(可选 - 默认为类的全限定名): 在查询语句中可以使用的名字。

  • 相关阅读:
    单例模式的double check写法中的volatile关键字
    java开发中避免NullPointerException
    java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext
    linux环境工作记录
    常用Java开发者工具
    compile once,run anywhere
    Java 线程
    常用的git命令
    oracle 随笔
    常用px,pt,em换算表
  • 原文地址:https://www.cnblogs.com/aaa6818162/p/1545845.html
Copyright © 2020-2023  润新知