hibernate的Component,即組件,表示2個類之間的關係,即其中1個類可以作爲另一個類的組件來使用。
1.先來看下annotation中關於component的API
2.2.2.3. 嵌入式對象(又名組件)
在實體中可以定義一個嵌入式組件(embedded component), 甚至覆蓋該實體中原有的列映射. 組件類必須在類一級定義@Embeddable註解. 在特定的實體的關聯屬性上使用@Embedded和@AttributeOverride註解可以覆蓋該屬性對應的嵌入式對象的列映射:
@Entity public 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; ... }
@Embeddable public class Address implements Serializable { String city; Country nationality; //no overriding here }
@Embeddable public 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 有兩個組件屬性, 分別是homeAddress和bornIn. 我們可以看到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.再來看下xml中關於component的API
The notion of a component is re-used in several different contexts and purposes throughout Hibernate.
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; } }
Our Hibernate mapping would look like this:
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name"> <!-- class attribute optional --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
人員(Person)表中將包括pid
, birthday
, initial
, first
和 last
等字段。
<component>
元素還允許有 <parent>
子元素,用來表明component類中的一個屬性是指向包含它的實體的引用。
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name" unique="true"> <parent name="namedPerson"/> <!-- reference back to the Person --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
<set name="someNames" table="some_names" lazy="true"> <key column="id"/> <composite-element class="eg.Name"> <!-- class attribute required --> <property name="initial"/> <property name="first"/> <property name="last"/> </composite-element> </set>
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.Purchase"> <property name="purchaseDate"/> <property name="price"/> <property name="quantity"/> <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional --> </composite-element> </set> </class>
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.OrderLine"> <many-to-one name="purchaseDetails class="eg.Purchase"/> <many-to-one name="item" class="eg.Item"/> </composite-element> </set> </class>
Composite elements can appear in queries using the same syntax as associations to other entities.
<class name="OrderLine"> <composite-id name="id" class="OrderLineId"> <key-property name="lineId"/> <key-property name="orderId"/> <key-property name="customerId"/> </composite-id> <property name="name"/> <many-to-one name="order" class="Order" insert="false" update="false"> <column name="orderId"/> <column name="customerId"/> </many-to-one> .... </class>
<many-to-one name="orderLine" class="OrderLine"> <!-- the "class" attribute is optional, as usual --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-one>
<set name="undeliveredOrderLines"> <key column name="warehouseId"/> <many-to-many class="OrderLine"> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-many> </set>
<set name="orderLines" inverse="true"> <key> <column name="orderId"/> <column name="customerId"/> </key> <one-to-many class="OrderLine"/> </set>
The <one-to-many>
element declares no columns.
<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 class="DeliveryAttempt"> ... </composite-element> </set> </class>
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" class="Baz" column="BAZ_ID"/> </dynamic-component>
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;
@Entity
public 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.xml中只需引用Husband
<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的xml的實例
仍然使用之前的Student和StudentCard,其中StudentCard作爲Student的component,StudentCard不需要xml配置文件,在Student中引用StudentCard,並在Student.hbm.xml使用
<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;
}
}
Studentpackage 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.xml
<?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="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.xml中只需引用Student.hbm.xml,StudentCard無對應的xml
<mapping resource="com/baosight/model/Student.hbm.xml"/>
仍然使用3中的JUnit測試,測試結果見3中。
以上即爲hibernate的component的基本用法,需要在實際地使用中仔細體會。