hibernate的Component,即組件,表示2個類之間的關係,即其中1個類能夠做爲另外一個類的組件來使用。html
1.先來看下annotation中關於component的APIjava
在實體中能夠定義一個嵌入式組件(embedded component), 甚至覆蓋該實體中原有的列映射. 組件類必須在類一級定義@Embeddable註解. 在特定的實體的關聯屬性上使用@Embedded和@AttributeOverride註解能夠覆蓋該屬性對應的嵌入式對象的列映射:api
@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).session
在上面的例子中,實體bean Person 有兩個組件屬性, 分別是homeAddress和bornIn. 咱們能夠看到homeAddress 屬性並無註解. 可是Hibernate自動檢測其對應的Address類中的@Embeddable註解, 並將其看做一個持久化組件.對於Country中已映射的屬性, 則使用@Embedded和@AttributeOverride 註解來覆蓋原來映射的列名. 正如你所看到的, Address對象中還內嵌了Country對象, 這裏和homeAddress同樣使用了Hibernate和EJB3自動檢測機制. 目前EJB3規範還不支持覆蓋多層嵌套(即嵌入式對象中還包括其餘嵌入式對象)的列映射. 不過Hibernate經過在表達式中使用"."符號表達式提供了對此特徵的支持.app
@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).ide
Hibernate如今支持在嵌入式對象中使用關聯註解(如@*ToOne和@*ToMany). 而EJB3規範尚不支持這樣的用法。你可使用 @AssociationOverride註解來覆寫關聯列.測試
在同一個實體中使用兩個同類型的嵌入對象, 其默認列名是無效的:至少要對其中一個進行明確聲明. Hibernate在這方面走在了EJB3規範的前面, Hibernate提供了NamingStrategy, 在使用Hibernate時, 經過NamingStrategy你能夠對默認的機制進行擴展. DefaultComponentSafeNamingStrategy 在默認的EJB3NamingStrategy上進行了小小的提高, 容許在同一實體中使用兩個同類型的嵌入對象而無須額外的聲明.ui
2.再來看下xml中關於component的APIthis
The notion of a component is re-used in several different contexts and purposes throughout Hibernate.spa
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 Person
. Name
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 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
等字段。
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 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>
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="eg.Name"> <!-- 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 purchaseDate
, price
andquantity
are properties of the association:
<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>
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 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.
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.
You can use a component as an identifier of an entity class. Your component class must satisfy certain requirements:
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" 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>
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" class="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 column
element just gives more declaration options, which are mostly useful when utilizing hbm2ddl
<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>
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 Configuration
object.
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; } }
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.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的基本用法,須要在實際地使用中仔細體會。