你的位置:首页 > Java教程

[Java教程]hibernate的component使用


hibernate的Component,即组件,表示2个类之间的关系,即其中1个类可以作为另一个类的组件来使用。

1.先来看下annotation中关于component的API

2.2.2.3. 嵌入式对象(又名组件)


 

在实体中可以定义一个嵌入式组件(embedded component), 甚至覆盖该实体中原有的列映射. 组件类必须在类一级定义@Embeddable注解. 在特定的实体的关联属性上使用@Embedded@AttributeOverride注解可以覆盖该属性对应的嵌入式对象的列映射:

@Entitypublic class Person implements Serializable {  // Persistent component using defaults  Address homeAddress;  @Embedded  @AttributeOverrides( {      @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),      @AttributeOverride(name="name", column = @Column(name="bornCountryName") )  } )  Country bornIn;  ...}      
@Embeddablepublic class Address implements Serializable {  String city;  Country nationality; //no overriding here}      
@Embeddablepublic class Country implements Serializable {  private String iso2;  @Column(name="countryName") private String name;  public String getIso2() { return iso2; }  public void setIso2(String iso2) { this.iso2 = iso2; }    public String getName() { return name; }  public void setName(String name) { this.name = name; }  ...}      

嵌入式对象继承其所属实体中定义的访问类型 (注意:这可以通过使用Hibernate提供的@AccessType注解来覆盖原有值)(请参考 Hibernate Annotation Extensions).

在上面的例子中,实体bean Person 有两个组件属性, 分别是homeAddressbornIn. 我们可以看到homeAddress 属性并没有注解. 但是Hibernate自动检测其对应的Address类中的@Embeddable注解, 并将其看作一个持久化组件.对于Country中已映射的属性, 则使用@Embedded@AttributeOverride 注解来覆盖原来映射的列名. 正如你所看到的, Address对象中还内嵌了Country对象, 这里和homeAddress一样使用了Hibernate和EJB3自动检测机制. 目前EJB3规范还不支持覆盖多层嵌套(即嵌入式对象中还包括其他嵌入式对象)的列映射. 不过Hibernate通过在表达式中使用"."符号表达式提供了对此特征的支持.

  @Embedded  @AttributeOverrides( {      @AttributeOverride(name="city", column = @Column(name="fld_city") ),      @AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),      @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )      //nationality columns in homeAddress are overridden  } )  Address homeAddress;

Hibernate注解支持很多EJB3规范中没有明确定义的特性. 例如,可以在嵌入式对象上添加 @MappedSuperclass注解, 这样可以将其父类的属性持久(详情请查阅@MappedSuperclass).

Hibernate现在支持在嵌入式对象中使用关联注解(如@*ToOne@*ToMany). 而EJB3规范尚不支持这样的用法。你可以使用 @AssociationOverride注解来覆写关联列.

在同一个实体中使用两个同类型的嵌入对象, 其默认列名是无效的:至少要对其中一个进行明确声明. Hibernate在这方面走在了EJB3规范的前面, Hibernate提供了NamingStrategy, 在使用Hibernate时, 通过NamingStrategy你可以对默认的机制进行扩展. DefaultComponentSafeNamingStrategy 在默认的EJB3NamingStrategy上进行了小小的提升, 允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.

 

2.再来看下

第 8 章 组件(Component)映射


8.1. 依赖对象(Dependent objects)
8.2. 在集合中出现的依赖对象 (Collections of dependent objects)
8.3. 组件作为Map的索引(Components as Map indices )
8.4. 组件作为联合标识符(Components as composite identifiers)
8.5. 动态组件 (Dynamic components)

The notion of a component is re-used in several different contexts and purposes throughout Hibernate.

8.1. 依赖对象(Dependent objects)


A component is a contained object that is persisted as a value type and not an entity reference. The term "component" refers to the object-oriented notion of composition and not to architecture-level components. For example, you can model a person like this:

public class Person {  private java.util.Date birthday;  private Name name;  private String key;  public String getKey() {    return key;  }  private void setKey(String key) {    this.key=key;  }  public java.util.Date getBirthday() {    return birthday;  }  public void setBirthday(java.util.Date birthday) {    this.birthday = birthday;  }  public Name getName() {    return name;  }  public void setName(Name name) {    this.name = name;  }  ......  ......}
public class Name {  char initial;  String first;  String last;  public String getFirst() {    return first;  }  void setFirst(String first) {    this.first = first;  }  public String getLast() {    return last;  }  void setLast(String last) {    this.last = last;  }  public char getInitial() {    return initial;  }  void setInitial(char initial) {    this.initial = initial;  }}

Now Name can be persisted as a component of PersonName defines getter and setter methods for its persistent properties, but it does not need to declare any interfaces or identifier properties.

Our Hibernate mapping would look like this:

<class name="eg.Person" table="person">  <id name="Key" column="pid" type="string">    <generator />  </id>  <property name="birthday" type="date"/>  <component name="Name" > <!-- class attribute optional -->    <property name="initial"/>    <property name="first"/>    <property name="last"/>  </component></class>

人员(Person)表中将包括pidbirthdayinitialfirst和 last等字段。

Like value types, components do not support shared references. In other words, two persons could have the same name, but the two person objects would contain two independent name objects that were only "the same" by value. The null value semantics of a component are ad hoc. When reloading the containing object, Hibernate will assume that if all component columns are null, then the entire component is null. This is suitable for most purposes.

The properties of a component can be of any Hibernate type (collections, many-to-one associations, other components, etc). Nested components should not be considered an exotic usage. Hibernate is intended to support a fine-grained object model.

<component> 元素还允许有 <parent>子元素,用来表明component类中的一个属性是指向包含它的实体的引用。

<class name="eg.Person" table="person">  <id name="Key" column="pid" type="string">    <generator />  </id>  <property name="birthday" type="date"/>  <component name="Name" unique="true">    <parent name="namedPerson"/> <!-- reference back to the Person -->    <property name="initial"/>    <property name="first"/>    <property name="last"/>  </component></class>

8.2. 在集合中出现的依赖对象 (Collections of dependent objects)


Collections of components are supported (e.g. an array of type Name). Declare your component collection by replacing the <element> tag with a <composite-element> tag:

<set name="someNames" table="some_names" lazy="true">  <key column="id"/>  <composite-element > <!-- class attribute required -->    <property name="initial"/>    <property name="first"/>    <property name="last"/>  </composite-element></set>

重要

If you define a Set of composite elements, it is important to implement equals() and hashCode()correctly.


Composite elements can contain components but not collections. If your composite element contains components, use the <nested-composite-element> tag. This case is a collection of components which themselves have components. You may want to consider if a one-to-many association is more appropriate. Remodel the composite element as an entity, but be aware that even though the Java model is the same, the relational model and persistence semantics are still slightly different.

A composite element mapping does not support null-able properties if you are using a <set>. There is no separate primary key column in the composite element table. Hibernate uses each column's value to identify a record when deleting objects, which is not possible with null values. You have to either use only not-null properties in a composite-element or choose a <list><map><bag> or <idbag>.

A special case of a composite element is a composite element with a nested <many-to-one> element. This mapping allows you to map extra columns of a many-to-many association table to the composite element class. The following is a many-to-many association from Order to Item, where purchaseDateprice andquantity are properties of the association:

<class name="eg.Order" .... >  ....  <set name="purchasedItems" table="purchase_items" lazy="true">    <key column="order_id">    <composite-element >      <property name="purchaseDate"/>      <property name="price"/>      <property name="quantity"/>      <many-to-one name="item" /> <!-- class attribute is optional -->    </composite-element>  </set></class>

There cannot be a reference to the purchase on the other side for bidirectional association navigation. Components are value types and do not allow shared references. A single Purchase can be in the set of anOrder, but it cannot be referenced by the Item at the same time.

其实组合元素的这个用法可以扩展到三重或多重关联:

<class name="eg.Order" .... >  ....  <set name="purchasedItems" table="purchase_items" lazy="true">    <key column="order_id">    <composite-element >      <many-to-one name="purchaseDetails />      <many-to-one name="item" />    </composite-element>  </set></class>

Composite elements can appear in queries using the same syntax as associations to other entities.


8.3. 组件作为Map的索引(Components as Map indices )


The <composite-map-key> element allows you to map a component class as the key of a Map. Ensure that you override hashCode() and equals() correctly on the component class.


8.4. 组件作为联合标识符(Components as composite identifiers)


You can use a component as an identifier of an entity class. Your component class must satisfy certain requirements:

  • 它必须实现java.io.Serializable接口

  • It must re-implement equals() and hashCode() consistently with the database's notion of composite key equality.


Note

In Hibernate3, although the second requirement is not an absolutely hard requirement of Hibernate, it is recommended.


You cannot use an IdentifierGenerator to generate composite keys. Instead the application must assign its own identifiers.

Use the <composite-id> tag, with nested <key-property> elements, in place of the usual <id> declaration. For example, the OrderLine class has a primary key that depends upon the (composite) primary key of Order.

<class name="OrderLine">    <composite-id name="id" >    <key-property name="lineId"/>    <key-property name="orderId"/>    <key-property name="customerId"/>  </composite-id>    <property name="name"/>    <many-to-one name="order"       insert="false" update="false">    <column name="orderId"/>    <column name="customerId"/>  </many-to-one>  ....  </class>

Any foreign keys referencing the OrderLine table are now composite. Declare this in your mappings for other classes. An association to OrderLine is mapped like this:

<many-to-one name="orderLine" ><!-- the "class" attribute is optional, as usual -->  <column name="lineId"/>  <column name="orderId"/>  <column name="customerId"/></many-to-one>

提示

The column element is an alternative to the column attribute everywhere. Using the columnelement just gives more declaration options, which are mostly useful when utilizing hbm2ddl


指向OrderLine多对多关联也使用联合外键:

<set name="undeliveredOrderLines">  <key column name="warehouseId"/>  <many-to-many >    <column name="lineId"/>    <column name="orderId"/>    <column name="customerId"/>  </many-to-many></set>

Order中,OrderLine的集合则是这样:

<set name="orderLines" inverse="true">  <key>    <column name="orderId"/>    <column name="customerId"/>  </key>  <one-to-many /></set>

The <one-to-many> element declares no columns.

假若OrderLine本身拥有一个集合,它也具有组合外键。

<class name="OrderLine">  ....  ....  <list name="deliveryAttempts">    <key>  <!-- a collection inherits the composite key type -->      <column name="lineId"/>      <column name="orderId"/>      <column name="customerId"/>    </key>    <list-index column="attemptId" base="1"/>    <composite-element >      ...    </composite-element>  </set></class>

8.5. 动态组件 (Dynamic components)


You can also map a property of type Map:

<dynamic-component name="userAttributes">  <property name="foo" column="FOO" type="string"/>  <property name="bar" column="BAR" type="integer"/>  <many-to-one name="baz" column="BAZ_ID"/></dynamic-component>

The semantics of a <dynamic-component> mapping are identical to <component>. The advantage of this kind of mapping is the ability to determine the actual properties of the bean at deployment time just by editing the mapping document. Runtime manipulation of the mapping document is also possible, using a DOM parser. You can also access, and change, Hibernate's configuration-time metamodel via the Configurationobject.

3.component的annotation的实例

仍然使用之前的Husband和Wife,其中Wife作为Husband的component,Wife不适用注解,在Husband中引用Wife,并在getWife上使用@Embedded

Wife

package com.baosight.model;public class Wife {  private String wifeId;  private String WifeName;  private String age;  public String getWifeId() {    return wifeId;  }  public void setWifeId(String id) {    this.wifeId = id;  }  public String getWifeName() {    return WifeName;  }  public void setWifeName(String name) {    this.WifeName = name;  }  public String getAge() {    return age;  }  public void setAge(String age) {    this.age = age;  }}

Husband

package com.baosight.model;import javax.persistence.Embedded;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;@Entitypublic class Husband {  private String id;  private String name;  private Wife wife;  @Id  @GeneratedValue//auto  public String getId() {    return id;  }  public void setId(String id) {    this.id = id;  }  public String getName() {    return name;  }  public void setName(String name) {    this.name = name;  }  @Embedded  public Wife getWife() {    return wife;  }  public void setWife(Wife wife) {    this.wife = wife;  }}

 注意,在hibernate.cfg.

<mapping class="com.baosight.model.Husband"/>

JUnit测试类OrMappingTest.java

package com.baosight.model;import org.hibernate.SessionFactory;import org.hibernate.cfg.AnnotationConfiguration;import org.hibernate.cfg.Configuration;import org.hibernate.tool.hbm2ddl.SchemaExport;import org.junit.AfterClass;import org.junit.BeforeClass;import org.junit.Test;public class OrMappingTest {  private static SessionFactory sf = null;  @BeforeClass  public static void beforeClass(){    // 读取配置文件    Configuration cfg = new AnnotationConfiguration();    // 得到session工厂    sf = cfg.configure().buildSessionFactory();  }  @Test  public void testSchemaExport() {    new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);  }  @AfterClass  public static void afterClass(){    // 关闭session工厂    sf.close();  }}

使用上类进行JUnit测试,结果为:

4.component的

仍然使用之前的Student和StudentCard,其中StudentCard作为Student的component,StudentCard不需要

<component name="card">
<property name="cardId"></property>
<property name="num"></property>
</component>

StudentCard

package com.baosight.model;public class StudentCard {  private String cardId;  private String num;  public String getCardId() {    return cardId;  }  public void setCardId(String id) {    this.cardId = id;  }  public String getNum() {    return num;  }  public void setNum(String num) {    this.num = num;  }  }

Student

package com.baosight.model;public class Student {  private String id;  private String name;  private int age;  private StudentCard card;//  private StudentPK pk;  public String getId() {    return id;  }  public void setId(String id) {    this.id = id;  }  public String getName() {    return name;  }  public void setName(String name) {    this.name = name;  }  public int getAge() {    return age;  }  public void setAge(int age) {    this.age = age;  }/*  public StudentPK getPk() {    return pk;  }  public void setPk(StudentPK pk) {    this.pk = pk;  }*/  public StudentCard getCard() {    return card;  }  public void setCard(StudentCard card) {    this.card = card;  }  }

Student.hbm.

<?mapping PUBLIC    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.baosight.model"><class name="Student" dynamic-update="true"><id name="id" ><generator class="native"></generator></id><!-- <property name="name"></property> --><property name="age"></property><component name="card"><property name="cardId"></property><property name="num"></property></component></class></hibernate-mapping>

注意,在hibernate.cfg.

<mapping resource="com/baosight/model/Student.hbm.

仍然使用3中的JUnit测试,测试结果见3中。

以上即为hibernate的component的基本用法,需要在实际地使用中仔细体会。