HibernateSpring 这两个突出的开源框架被越来越多的应用到 J2EE 中。尽管目标有着不同的问题空间,它们却共享一个关键特性:依赖注入。在对象返回到客户端之前 Spring 协助挑选出这些对象间依赖关系,减少客户端代码量。而 Hibernate 专门挑选出在完整的对象模型返回客户端之前由数据模型表现的依赖关系。当使用 JDBC 直接从数据模型映射到对象模型时,我们通常需要书写大量的代码来构建对象模型。Hibernate 的出现淘汰了这种繁琐的编码工作。
   
    Hibernate 2.x 提供基本的表到对象的映射,标准关联映射(包括 one-to-one, one-to-many 以及 many-to-many 关系),多态映射,等等。Hibernate 3.x 沿着路线继续前进,formula、filter、subselect 等,使映射更加灵活,提供用细粒度的解释特性。
   
    在本文中,将阐述 formula 到底有哪些特性可帮助我们进行模型转换。Hibernate 3.x 之前,formula 属性只能出现在 property 元素中。但是到了现在,你可以在许多元素中使用 Hibernate 3.x 提供的 formula 属性或元素(formula 用法方面都是一样的),包括 discriminator、element、many-to-many、map-key、map-key-many-to-many、以及 property。它增加了 OR 映射的灵活性,因此允许对复杂数据模型更加细粒的解释。
   
    下面有两个 formula 应用场景:
   
    1. formula 用于评估结果的场合。在 discriminator、element、map-key、map-key-many-to-many以及 property 元素中注入 formula。
    2. formula 用于连接目的的场合。在 many-to-one、one-to-one 以及 many-to-many 元素中注入 formula。
   
范畴 1:从 formula 获得评估结果
   
    Discriminator
   
    在真实的数据 schema 中,经常出现一个表被用于描述其他表的情况。formula 可协助提供灵活的多态 OR 映射。
   
    在图 1 的范例中,有两个表:Product 和 ProductRelease。每条 product 记录都有一个 ProductReleaseID 参考相应的产品出厂记录,包括 product release name、type、release date 等等。

          Product and Product Release Data Model
                       图 1. Product 和 product release
   
    ProductRelease 表中有个有趣的属性 SubProductAllowable,该属性的值为 1 或 0。值为 1 代表允许任何的次品出厂,但是 0 不允许次品出厂。
   
    图 2 展示了由数据模型解释的对象模型。Nested 接口定义了 getSubProducts 和 setSubProducts 方法。NestedProduct 类继承 Product 基类并实现 Nested 接口。无论产品数据记录是 Product 或 NestedProduct,都取决于产品出厂记录中 SubProductAllowable 的值。

        Product and Product Release Object Domain Model
                     图 2. Product 和产品出厂对象域模型
   
    为了完成模型转换,我们使用如下的 Hibernate 3.x 映射:
    
<hibernate-mapping>
  
<class name="Product" 
        discriminator-value
="0"  lazy="false">
    
<id name="id" type="long"/>       
    
<discriminator 
        
formula="(select pr.SubProductAllowable 
                from ProductRelease pr 
                where pr.productReleaseID=
                        productReleaseID)"

        type
="integer" />
    
<subclass  name="NestedProduct"  
        discriminator-value
="1"/>
  
</class>
</hibernate-mapping>

    如果 formula 表达式评估结果为 0 时--也就是不允许次品出厂--那么对象将是 Product 类。如果结果是 1,那么对象将是 NestedProduct。在表 1 和 2 中,表 Product 的第一条记录(ProductID=10000001),已初始化的类将是 NestedProduct,因为它参考一条 SubProductAllowable=1 的 ProductRelease 记录。表 Product 的第二条记录(ProductID=20000001),已初始化的类将是 Product,因为它参考一条 SubProductAllowable=0 的 ProductRelease 记录。
   
S/N ProductReleaseID SubProductAllowable ...
1 11 1 ¡­
2 601 0 ¡­
        表 1. ProductRelease 表中的记录
   
S/N ProductID ProductReleaseID ...
1 10000001 11 ¡­
2 20000001 601 ...
        表 2. Product 表中的记录
   
    Property
   
    在 property 元素中的 formula 允许对象属性包含特定引伸值,比如对结果进行 sum、average、max 等等。

<property name="averagePrice" formula="(select avg(pc.price) from PriceCatalogue pc, SelectedItems si where si.priceRefID=pc.priceID)"/>

    此外,formula 也能协助从基于当前记录的特定值向其它表检索数据。例如:

<property name="currencyName" formula="(select cur.name from currency cur where cur.id= currencyID)"/>

    这将由助于从 currency 表检索 currency name。正如你所看到的,这样直接映射可消除许多转换编码。
   
    map-key
   
    formula 允许 map-key 持有任何可能的值。下列范例(图 3),我们想让 Role_roleID 成为对象模型的 map-key(图 4)。

                 User Role Data Schema   
                          图 3. 用户角色数据 schema

   User Role Object Model
                            图 4. 用户角色对象模型
   
    在前面的数据 schema 中,User 和 Role 由 User_has_Role 通过 many-to-many 关系关联调用。为了获取某个 User 所有的指派角色,我们进行如下映射:

<hibernate-mapping>
  
<class name="User">
    
<id name="userID"/>
    
<map name="roles"  
        table
="UserRole"/>
      
<key column="User_userID"/>
      
<map-key 
        
formula="Role_RoleID" 
        type
="string"/>
      
<many-to-many 
        
column="Role_RoleID"
        class
="Role"/>
    
</map>
  
</class>
  
<class name="Role">
    
<id name="roleID"/>
  
</class>
</hibernate-mapping>

    Role_RoleID 通常用于连接 many-to-many 元素的栏位值。Hibernate 通常不允许 Role_RoleID 出现在 map-key 和 many-to-many 的栏位属性中。但是有了 formula,Role_RoleID 也能用于 map-key。
   
    element、map-key-many-to-many 以及其他
   
    element 和 property 类似,能从任何有效的 formula 表达式赋予评估值。
   
    map-key-many-to-many 中 formula 的用法类似于 map-key。但是,map-key-many-to-many 通常用于三重关系,map key 是一个被自己参考的对象,而不是被参考的属性。
   
    那么,到底哪些情况下 formula 不支持呢?有些数据库(例如 Oracle 7)就不支持嵌入式 select 语句(也就是说一条 select SQL 内嵌在另外一条 select SQL 语句中),这种情况 formula 就不支持评估结果。因此,你应该首先检查嵌入式 select SQL 语句是否被支持。
   
    一旦 Hibernate 的映射拿 formula 表达式作为 select SQL 选取的一部分,请确认数据库 SQL 方言是否允许使用 formula,尽管这样将降低代码轻便性。
   
范畴 2:formula 用于连接

    many-to-one
   
    另一个普遍的场景是真实世界的数据模型是所有者关系映射,这意味着映射是不同于 one-to-one、one-to-many 以及 many-to-many 关系的,formula 是提供所有者关系管理元素中的一个。图 5 展示了某公司可有多个联系人,但是其中只有一个为默认联系人的范例。一个公司有多个联系人是典型的 one-to-many 关系。但是,为了标识默认联系人,ContactPerson 表使用了 defaultFlag 参数(1 是 yes, 0是 no)。

               
                        图 5. 用户角色数据 schema

             
                           图 6. 用户角色对象模型
   
    为了说明对象模型(图 6)中默认联系人关系,我们使用如下映射文件:

<hibernate-mapping>
  
<class name="Company" table="Company">
    
<id name="id" />
    
<many-to-one 
      
name="defaultContactPerson" 
      property-ref
="defaultContactPerson">
        
<column name="id"/>
        
<formula>1</formula>
        
</many-to-one>
  
</class>
  
<class name="Person" >
    
<id name="id" />
    
<properties name="defaultContactPerson">
        
<property name="companyID" />
        
<property name="defaultFlag" />
    
</properties>
  
</class>
</hibernate-mapping>

    如上,我们把 companyID 和 defaultFlag 组织到名为 defaultContactPerson 的 properties 元素中,做为 Person 表的 unique key。Company 类中的 many-to-one 元素连接 Person 表的 defaultContactPerson properties 元素。输出的 SQL 像这样:
   
    select c.id, p.id from Company c, Person p where p.companyID=c.id and p.defaultFlag=1
   
    one-to-one
   
    Hibernate 中,one-to-one 主要用于两张表共享同一主键的情况。对于外键关联,我们通常使用 many-to-one 来代替。但是,有了 formula,one-to-one 可以通过外键连接表。上面的 many-to-one 范例可以通过 one-to-one 来映射:

<hibernate-mapping>
  
<class name="Company" table="Company" >
    
<id name="id" />
    
<one-to-one name="defaultContactPerson" 
        property-ref
="defaultContactPerson" >
    
<formula>id</formula>
    
<formula>1</formula>
    
</one-to-one>
  
</class>
  
<class name="Person" >
    
<id name="id" />
    
<properties name="defaultContactPerson">
      
<property name="companyID" />
      
<property name="defaultFlag" />
    
</properties>
   
</class>
</hibernate-mapping>

    many-to-many
   
    formula 用于当 many-to-many 元素为关系表和实体表连接的特殊关系,尽管通常没有必要这样用。
   
总结

    文章范例展示了大部分 formula 的适用情景。当需要 formula 评估值时,formula 表达式将出现在 产生的 SQL 语句的 select 部分。当 formula 用于连接时,它出现在产生的 SQL 语句的 where 部分。此外,formula 表达式可用于任何 SQL 方言,只要目标数据库支持。最后,formula 可协助完成从数据模型到对象模型的无代码细粒度映射。


请注意!引用、转贴本文应注明原译者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen