• [NHibernate]O/R Mapping基础


    系列文章

    [Nhibernate]体系结构

    [NHibernate]ISessionFactory配置

    [NHibernate]持久化类(Persistent Classes)

    引言

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

    请注意,虽然很多Hibernate用户选择手工定义XML映射文档,也有一些工具来生成映射文档,包括XDoclet,Middlegn和AndroMDA(这里是Nhibernate文档中移除没有从Hibernate文档中转换过来的部分),NHibernate中并没有像XDoclet,Middlegn和AndroMDA这样的工具,在运用中,一般使用代码生成器来生成XML配置文档。

    一个映射的例子

     1 <?xml version="1.0" ?>
     2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"
     3  namespace="Eg" assembly="Eg">
     4  <class name="Cat" table="CATS" discriminator-value="C">
     5   <id name="Id" column="uid" type="Int64">
     6    <generator class="hilo"/>
     7   </id>
     8   <discriminator column="subclass" type="Char"/>
     9   <property name="Birthdate" type="Date"/>
    10   <property name="Color" not-null="true"/>
    11   <property name="Sex" not-null="true" update="false"/>
    12   <property name="Weight"/>
    13   <many-to-one name="Mate" column="mate_id"/>
    14   <set name="Kittens">
    15    <key column="mother_id"/>
    16    <one-to-many class="Cat"/>
    17   </set>
    18   <subclass name="DomesticCat" discriminator-value="D">
    19    <property name="Name" type="String"/>
    20   </subclass>
    21  </class>
    22  <class name="Dog">
    23   <!-- mapping for Dog could go here -->
    24  </class>
    25 </hibernate-mapping>

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

    Schema

    所有的XML映射都需要使用nhibenate-mapping-2.0 schema。目前schema可以在NHibernate的资源路径或者是NHibernate.dll的嵌入资源(EmbeddedResource)中找到。NHibernate总是会优先使用嵌入在资源中的schema文件。

    在使用VisualStudio.Net时,你应该将hibernate-mapping拷贝到C:ProgramFilesMicrosoft Visual Studio.NET2003Common7Packagesschemasxml路径中,以获取智能感知功能。或者在解决方案中,新建一个解决方案文件夹,将hibernate-mapping拷入该解决方案文件夹中也可以获得智能感知功能。

    hibernate-mapping

    这个元素包括四个可选的属性。schema属性,指明了这个映射所引用的表所在的schema名称。假若指定了这个属性,表名会加上所指定的schema的名字扩展schema的名字扩展为全限定名。假若没有指定,表明就不会使用全限定名。default-cascade指定了未注明cascade属性和集合类会采用什么样的默认级联风格。auto-import属性默认让我们在查询语言中可以使用非全限定名的类名。default-access告诉我们怎么访问属性。

    1 <hibernate-mapping
    2  schema="schemaName" (1)
    3  default-cascade="none|save-update" (2)
    4  auto-import="true|false" (3)
    5  default-access="property|field|nosetter|ClassName" (4)
    6  assembly="assembly.name" (5)
    7  namespace="namespace.name" (6)
    8 > 

    (1)schema(可选):数据库schema名称。

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

    (3)auto-import(可选-默认为true):指定是否我们可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。

    (4)default-acess(可选-默认为property):NHibernate访问属性时的策略。

    (5)assembly(可选):指定一个程序集,如果在映射文档中没有指定程序集,就使用这个程序集。

    (6)namespace(可选):指定一个命名空间前缀,如果在一个映射文档中没有指定全限定名,就使用这个命名空间名。

    假若你有两个持久化类,他们的非全限定名是一样的(就是在不同的命名空间里面),你应该设置auto-import=“false”。假若说你把一个“import过”的名字同时对应两个类,NHibernate会抛出一个异常。

    class

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

     1 <class
     2  name="ClassName" (1)
     3  table="tableName"(2)
     4  discriminator-value="discriminator_value"(3)
     5  mutable="true|false"(4)
     6  schema="owner"(5)
     7  proxy="ProxyInterface"(6)
     8  dynamic-update="true|false"(7)
     9  dynamic-insert="true|false"(8)
    10  polymorphism="implicit|explicit"(9)
    11  where="arbitrary sql where condition"(10)
    12  persister="PersisterClass"(11)
    13  lazy="true|false"(12)
    14 />

    (1)name:持久化类(或者接口)的全限定名。
    (2)table:对应的数据库表名。

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

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

    (5)schema(可选):覆盖在根<hibernate-mapping>元素中指定的schema名字。

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

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

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

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

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

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

    (12)lazy(可选):假若设置lazy=“true”,就是设置这个类自己的名字作为proxy接口的一种等价快捷方式。

    若指明的持久化类实际上是一个接口,也可以被完美的接受。其后你可以用<subclass>来指定该接口的实际实现类名。你可以持久化任static(静态的)内部类。记得应该使用标准的类名格式,就是说比如:Eg.Foo+Bar.

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

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

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

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

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

    id 

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

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

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

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

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

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

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

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

    unsaved-value属性很重要!如果你的类的标识属性不是默认为null的,你应该指定正确的默认值。特别重要的是在使用值类型System.ValueType,例如System.Int32或者System.Guid作为你的<id>属性时确保清楚的设置这个属性,因为System.ValueType对象不可能为null值。

    还有一个另外的<composite-id>声明可以访问旧式的多主键数据。不鼓励使用这种方式。

    gennerator

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

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

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

    identity

     

    对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。返回的标识符是 Int64, Int32 或者 Int16类型的。

    sequence(序列)

    对DB2,MySQL, PostgreSQL, Oracle的内置标识字段提供支持。返回的标识符是Int64 Int32 或者 Int16类型的。

    hilo(高低位)

    使用一个高/低位算法来高效的生成Int64, Int32 或者 Int16类型的标识符。给定一个表和字段(默认分别是hibernate_unique_key 和next)作为高位值得来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。

    seqhilo(使用序列的高低位)

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

    uuid.hex

     

    用一个System.Guid和它的ToString(string format)方法生成字符串类型的标识符。字符串的长度取决于 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>联合一起使用。

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

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

     1 <id name="Id" type="Int64" column="cat_id">
     2  <generator class="hilo">
     3   <param name="table">hi_value</param>
     4   <param name="column">next_value</param>
     5   <param name="max_lo">100</param>
     6  </generator>
     7 </id>
     8 <id name="Id" type="Int64" column="cat_id">
     9  <generator class="seqhilo">
    10   <param name="sequence">hi_value</param>
    11   <param name="max_lo">100</param>
    12  </generator>
    13 </id>

    很不幸,你在为NHibernate自行提供Connection时无法使用hilo。Hibernate必须能够在一个新的事务中得到一个“hi”值。

    UUID Hex算法 

    1 <id name="Id" type="String" column="cat_id">
    2  <generator class="uuid.hex">
    3   <param name="format">format_value</param>
    4   <param name="seperator">seperator_value</param>
    5  </generator>
    6 </id>

    UUID是通过调用Guid.NewGuid().ToString(format)产生的。format值的设置请参考MSDN文档。默认的seperator很少也不应该被改变。format决定是否配置好的seperator,并提供自己使用。

    UUID String算法

    UUID是通过调用Guid.NewGuid().ToByteArray()并且把byte[]转换成char[],char[]作为一个16个字符组成的字符串返回。

     GUID算法

    guid标识符通过调用Guid.NewGuid()产生。为了提升Guids在MS SQL中作为主键,外键和索引的一部分时的性能,可以使用guid.comb。在别的数据库中使用guid.comb的好处是支持非标准的GUID。

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

    对于内部支持标识字段的数据库(DB2,MySQL,Sybase,MS SQL),你可以使用identity关键字生成。对于内部支持序列的数据库(DB2,Oracle, PostgreSQL),你可以使用sequence风格的关键字生成。这两种方式对于插入一个新的对象都需要两次SQL查询。当使用MS SQL并且采用identity主键生成器,select SCOPE_IDENTITY()将会被附加到insert的sql语句,因而不可避免的执行两个不同的IDbCommand。

    1 <id name="Id" type="Int64" column="uid">
    2  <generator class="sequence">
    3    <param name="sequence">uid_sequence</param>
    4  </generator>
    5 </id>
    6 <id name="Id" type="Int64" column="uid" unsaved-value="0">
    7  <generator class="identity"/>
    8 </id>

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

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

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

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

    联合ID(composite-id)

    1 <composite-id
    2  name="propertyName"(1)
    3  class="ClassName"(2)
    4  unsaved-value="any|none"(3)
    5  access="field|property|nosetter|ClassName">
    6  <key-property name="propertyName" type="typename" column="column_name"/>
    7  <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
    8  ......
    9 </composite-id>

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

    1 <composite-id>
    2  <key-property name="medicareNumber"/>
    3  <key-property name="dependent"/>
    4 </composite-id>

    你的持久化类必须重载Equals()和HashCode()方法,来实现组合的标识符判断等价.也必须实现Serializable接口

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

    (1)

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

    (2)

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

    (3)

    unsaved-value (可选 - 默认为 none): 假如被设置为any的值,就表示新创建,尚未被持久化的实例将持有的值。

     识别器(discriminator)

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

    1 <discriminator
    2  column="discriminator_column"(1)
    3  type="discriminator_type"(2)
    4  force="true|false"(3)
    5  insert="true|false" (4)
    6 />

    (1)

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

    (2)

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

    (3)

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

    (4)

    insert (可选 - 默认为 true) 当识别器是被映射的组件的标识符的一部分时设置为false。

    标识器字段的实际值是根据<class> 和<subclass>元素的discriminator-value得来的。

    总结

    ORM深入学习。

    本文来自

    《NHibernate中文文档》

  • 相关阅读:
    C语言文法分析
    词法分析--编程感想
    词法分析(改)
    0916词法分析
    0909关于编译
    python正则表达式
    python除法运算
    python值相等和对象相同
    Java编写程序时要考虑到所有可能的异常
    hadoop 读取文件操作
  • 原文地址:https://www.cnblogs.com/wolf-sun/p/3705229.html
Copyright © 2020-2023  润新知