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

5.1. 映射定义(Mapping declaration)

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

请注意,虽然很多Hibernate用户选择手写XML映射文档,但也有一些工具可以用来生成映射文档, 包括XDoclet,Middlegen和AndroMDA。

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

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
      "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

        <class name="Cat" 
            table="cats"
            discriminator-value="C">
                
                <id name="id">
                        <generator class="native"/>
                </id>

                <discriminator column="subclass" 
                     type="character"/>

                <property name="weight"/>

                <property name="birthdate"
                    type="date" 
                    not-null="true" 
                    update="false"/>

                <property name="color"
                    type="eg.types.ColorUserType"
                    not-null="true"
                    update="false"/>

                <property name="sex"
                    not-null="true" 
                    update="false"/>

                <property name="litterId"
                    column="litterId"
                    update="false"/>

                <many-to-one name="mother"
                    column="mother_id"
                    update="false"/>

                <set name="kittens"
                    inverse="true"
                    order-by="litter_id">
                        <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>

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

5.1.1. Doctype

所有的XML映射都需要定义如上所示的doctype。DTD可以从上述URL中获取, 也可以从hibernate-x.x.x/src/net/sf/hibernate目录中、 或hibernate.jar文件中找到。Hibernate总是会首先在它的classptah中搜索DTD文件。 如果你发现它是通过连接Internet查找DTD文件,就对照你的classpath目录检查XML文件里的DTD声明。

5.1.2. hibernate-mapping

这个元素包括一些可选的属性。schemacatalog属性, 指明了这个映射所连接(refer)的表所在的schema和/或catalog名称。 假若指定了这个属性,表名会加上所指定的schema和catalog的名字扩展为全限定名。假若没有指定,表名就不会使用全限定名。 default-cascade指定了未明确注明cascade属性的Java属性和 集合类Hibernate会采取什么样的默认级联风格。auto-import属性默认让我们在查询语言中可以使用 非全限定名的类名。

<hibernate-mapping
         schema="schemaName"                          (1)
         catalog="catalogName"                        (2)
         default-cascade="cascade_style"              (3)
         default-access="field|property|ClassName"    (4)
         default-lazy="true|false"                    (5)
         auto-import="true|false"                     (6)
         package="package.name"                       (7)
 />
(1)

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

(2)

catalog (可选): 数据库catalog的名称。

(3)

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

(4)

default-access (可选 - 默认为 property): Hibernate用来访问所有属性的策略。可以通过实现PropertyAccessor接口 自定义。

(5)

default-lazy (可选 - 默认为 true): 指定了未明确注明lazy属性的Java属性和集合类, Hibernate会采取什么样的默认加载风格。

(6)

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

(7)

package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。

假若你有两个持久化类,它们的非全限定名是一样的(就是两个类的名字一样,所在的包不一样--译者注), 你应该设置auto-import="false"。如果你把一个“import过”的名字同时对应两个类, Hibernate会抛出一个异常。

注意hibernate-mapping 元素允许你嵌套多个如上所示的 <class>映射。但是最好的做法(也许一些工具需要的)是一个 持久化类(或一个类的继承层次)对应一个映射文件,并以持久化的超类名称命名,例如: Cat.hbm.xmlDog.hbm.xml,或者如果使用继承,Animal.hbm.xml

5.1.3. class

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

<class
        name="ClassName"                              (1)
        table="tableName"                             (2)
        discriminator-value="discriminator_value"     (3)
        mutable="true|false"                          (4)
        schema="owner"                                (5)
        catalog="catalog"                             (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)
        entity-name="EntityName"                      (17)
        check="arbitrary sql check condition"         (18)
        rowid="rowid"                                 (19)
        subselect="SQL expression"                    (20)
        abstract="true|false"                         (21)
        node="element-name"
/>
(1)

name (可选): 持久化类(或者接口)的Java全限定名。 如果这个属性不存在,Hibernate将假定这是一个非POJO的实体映射。

(2)

table (可选 - 默认是类的非全限定名): 对应的数据库表名。

(3)

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

(4)

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

(5)

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

(6)

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

(7)

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

(8)

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

(9)

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

(10)

select-before-update (可选, 默认为 false): 指定Hibernate除非确定对象真正被修改了(如果该值为true-译注),否则不会执行SQL UPDATE操作。在特定场合(实际上,它只在一个瞬时对象(transient object)关联到一个 新的session中时执行的update()中生效),这说明Hibernate会在UPDATE 之前执行一次额外的SQL SELECT操作,来决定是否应该执行 UPDATE

(11)

polymorphism(多态) (可选, 默认值为 implicit (隐式) ): 界定是隐式还是显式的使用多态查询(这只在Hibernate的具体表继承策略中用到-译注)。

(12)

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

(13)

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

(14)

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

(15)

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

(16)

lazy (可选): 通过设置lazy="false", 所有的延迟加载(Lazy fetching)功能将被全部禁用(disabled)。

(17)

entity-name (可选,默认为类名): Hibernate3允许一个类进行多次映射( 前提是映射到不同的表),并且允许使用Maps或XML代替Java层次的实体映射 (也就是实现动态领域模型,不用写持久化类-译注)。 更多信息请看第 4.4 节 “动态模型(Dynamic models)” and 第 18 章 XML映射

(18)

check (可选): 这是一个SQL表达式, 用于为自动生成的schema添加多行(multi-row)约束检查

(19)

rowid (可选): Hibernate可以使用数据库支持的所谓的ROWIDs,例如: Oracle数据库,如果你设置这个可选的rowid, Hibernate可以使用额外的字段rowid实现快速更新。ROWID是这个功能实现的重点, 它代表了一个存储元组(tuple)的物理位置。

(20)

subselect (可选): 它将一个不可变(immutable)并且只读的实体映射到一个数据库的 子查询中。当你想用视图代替一张基本表的时候,这是有用的,但最好不要这样做。更多的介绍请看下面内容。

(21)

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

若指明的持久化类实际上是一个接口,这也是完全可以接受的。 之后你可以用元素<subclass>来指定该接口的实际实现类。 你可以持久化任何static(静态的)内部类。 你应该使用标准的类名格式来指定类名,比如:Foo$Bar

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

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

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

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

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

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

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

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

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

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

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

我们非常强烈建议你在Hibernate中使用version/timestamp字段来进行乐观锁定。 对性能来说,这是最好的选择,并且这也是唯一能够处理在session外进行操作的策略(例如: 在使用Session.merge()的时候)。

对Hibernate映射来说视图和表是没有区别的,这是因为它们在数据层都是透明的( 注意:一些数据库不支持视图属性,特别是更新的时候)。有时你想使用视图,但却不能在数据库 中创建它(例如:在遗留的schema中)。这样的话,你可以映射一个不可变的(immutable)并且是 只读的实体到一个给定的SQL子查询表达式:

<class name="Summary">
    <subselect>
        select item.name, max(bid.amount), count(*)
        from item
        join bid on bid.item_id = item.id
        group by item.name
    </subselect>
    <synchronize table="item"/>
    <synchronize table="bid"/>
    <id name="name"/>
    ...
</class>

定义这个实体用到的表为同步(synchronize),确保自动刷新(auto-flush)正确执行, 并且依赖原实体的查询不会返回过期数据。<subselect>在属性元素 和一个嵌套映射元素中都可见。

5.1.4. id

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

<id
        name="propertyName"                                          (1)
        type="typename"                                              (2)
        column="column_name"                                         (3)
        unsaved-value="null|any|none|undefined|id_value"             (4)
        access="field|property|ClassName"                            (5)
        node="element-name|@attribute-name|element/@attribute|.">

        <generator class="generatorClass"/>
</id>
(1)

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

(2)

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

(3)

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

(4)

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

(5)

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

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

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

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

5.1.4.1. Generator

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

<id name="id" type="long" column="cat_id">
        <generator class="org.hibernate.id.TableHiLoGenerator">
                <param name="table">uid_table</param>
                <param name="column">next_hi_value_column</param>
        </generator>
</id>

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

increment

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

identity

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

sequence

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

hilo

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

seqhilo

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

uuid

用一个128-bit的UUID算法生成字符串类型的标识符, 这在一个网络中是唯一的(使用了IP地址)。UUID被编码为一个32位16进制数字的字符串。

guid

在MS SQL Server 和 MySQL 中使用数据库生成的GUID字符串。

native

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

assigned

让应用程序在save()之前为对象分配一个标示符。这是 <generator>元素没有指定时的默认生成策略。

select

通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。

foreign

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

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

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

<id name="id" type="long" 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="long" column="cat_id">
        <generator class="seqhilo">
                <param name="sequence">hi_value</param>
                <param name="max_lo">100</param>
        </generator>
</id>

很不幸,你在为Hibernate自行提供Connection时无法使用hilo。 当Hibernate使用JTA获取应用服务器的数据源连接时,你必须正确地配置 hibernate.transaction.manager_lookup_class

5.1.4.3. UUID算法(UUID Algorithm )

UUID包含:IP地址,JVM的启动时间(精确到1/4秒),系统时间和一个计数器值(在JVM中唯一)。 在Java代码中不可能获得MAC地址或者内存地址,所以这已经是我们在不使用JNI的前提下的能做的最好实现了。

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

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

<id name="id" type="long" column="person_id">
        <generator class="sequence">
                <param name="sequence">person_id_sequence</param>
        </generator>
</id>
<id name="id" type="long" column="person_id" unsaved-value="0">
        <generator class="identity"/>
</id>

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

 

property

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

<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)
        lazy="true|false"                                            (7)
        unique="true|false"                                          (8)
        not-null="true|false"                                        (9)
        optimistic-lock="true|false"                                 (10)
        generated="never|insert|always"                              (11)
        node="element-name|@attribute-name|element/@attribute|."

        index="index_name"
        unique_key="unique_key_id"
        length="L"
        precision="P"
        scale="S"
/>
(1)

name: 属性的名字,以小写字母开头。

(2)

column (可选 - 默认为属性名字): 对应的数据库字段名。 也可以通过嵌套的<column>元素指定。

(3)

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

(4)

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

(5)

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

(6)

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

(7)

lazy (可选 - 默认为 false): 指定 指定实例变量第一次被访问时,这个属性是否延迟抓取(fetched lazily)( 需要运行时字节码增强)。

(8)

unique (可选): 使用DDL为该字段添加唯一的约束。 同样,允许它作为property-ref引用的目标。

(9)

not-null (可选): 使用DDL为该字段添加可否为空(nullability)的约束。

(10)

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

(11)

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

typename可以是如下几种:

  1. Hibernate基本类型名(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。

  2. 一个Java类的名字,这个类属于一种默认基础类型 (比如: int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob)。

  3. 一个可以序列化的Java类的名字。

  4. 一个自定义类型的类的名字。(比如: com.illflow.type.MyCustomType)。

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

access属性用来让你控制Hibernate如何在运行时访问属性。在默认情况下, Hibernate会使用属性的get/set方法对(pair)。如果你指明access="field", Hibernate会忽略get/set方法对,直接使用反射来访问成员变量。你也可以指定你自己的策略, 这就需要你自己实现org.hibernate.property.PropertyAccessor接口, 再在access中设置你自定义策略类的名字。

衍生属性(derive propertie)是一个特别强大的特征。这些属性应该定义为只读,属性值在装载时计算生成。 你用一个SQL表达式生成计算的结果,它会在这个实例转载时翻译成一个SQL查询的SELECT 子查询语句。

<property name="totalPrice"
    formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
                WHERE li.productId = p.productId
                AND li.customerId = customerId
                AND li.orderNumber = orderNumber )"/>

注意,你可以使用实体自己的表,而不用为这个特别的列定义别名( 上面例子中的customerId)。同时注意,如果你不喜欢使用属性, 你可以使用嵌套的<formula>映射元素。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值