一、PO的數(shù)據(jù)類型設(shè)置
int 還是Integer Integer 允許為 null
Hibernate 既可以訪問(wèn)Field也可以訪問(wèn)Property,訪問(wèn)Property是只是調(diào)用getXXX()、setXXX()方法,因此在from Customer where c.name=’Tom’ HQL中,name屬性不需要存在,只要getName()存在就可以了。
二、Hibernate映射
1、映射復(fù)合主鍵
代碼
-
主鍵類 ??
-
Public?class?CustomerId?implements?Serializable{ ??
-
????Private?final?String?name; ??
-
????Private?final?String?companyid; ??
-
} ??
-
映射文件 ??
-
<
class
?
name
=”test.Customer”?
table
=”CUSTOMERS”
>
??
-
????
<
composite-id
?
name
=”customerId”?
class
=”test.CustomerId”
>
??
-
????????
<
key-property
?
name
=”name”?
column
=”NAME”?
type
=”string”
/>
??
-
????????
<
key-property
?
name
=”companyId”?
column
=”COMPANY_ID”??
type
=”long”
/>
??
-
????
</
composite-id
>
??
-
????
<
version
?
name
=”varsion”?
column
=”VERSION”?
unsaved-value
=”0”
/>
??
-
????
<
many-to-one
?
name
=”company”?
class
=”test.Company”?
column
=”COMPANY_ID”?
insert
=”false”?
update
=”false”
/>
??
-
????
<
set
?
name
=”orders”?
lazy
=”true”?
inverse
=”true”
>
??
-
????????
<
key
>
??
-
????????????
<
column
column
=”NAME”
/>
??
-
????????????
<
column
column
=”COMPANY_ID”
/>
??
-
????????
</
key
>
??
-
????
</
set
>
??
-
</
class
>
??
-
<
class
?
name
=”test.Order”?
table
=”O(jiān)RDERS”
>
??
-
????
<
many-to-one
?
name
=”customer”?
class
=”test.Customer”
>
??
-
????????????
<
column
column
=”NAME”
/>
??
-
????????????
<
column
column
=”COMPANY_ID”
/>
??
-
????
</
many-to-one
>
??
-
</
class
>
??
-
??
-
或 ??
-
??
-
<
class
?
name
=”test.Customer”?
table
=”CUSTOMERS”
>
??
-
????
<
composite-id
?
name
=”customerId”?
class
=”test.CustomerId”
>
??
-
????????
<
key-property
?
name
=”name”?
column
=”NAME”?
type
=”string”
/>
??
-
<
key-many-to-one
?
name
=”company”?
class
=”test.Company”?
column
=”COMPANY_ID”
/>
??
-
??
-
????
</
composite-id
>
??
-
????
<
version
?
name
=”varsion”?
column
=”VERSION”?
unsaved-value
=”0”
/>
??
-
????
<
set
?
name
=”orders”?
lazy
=”true”?
inverse
=”true”
>
??
-
????????
<
key
>
??
-
????????????
<
column
column
=”NAME”
/>
??
-
????????????
<
column
column
=”COMPANY_ID”
/>
??
-
????????
</
key
>
??
-
????
</
set
>
??
-
</
class
>
??
-
<
class
?
name
=”test.Order”?
table
=”O(jiān)RDERS”
>
??
-
????
<
many-to-one
?
name
=”customer”?
class
=”test.Customer”
>
??
-
????????????
<
column
column
=”NAME”
/>
??
-
????????????
<
column
column
=”COMPANY_ID”
/>
??
-
????
</
many-to-one
>
??
-
</
class
>
??
2、映射組成關(guān)系
代碼
-
<
hibernate-mapping
>
??
-
????
<
class
?
name
=”Customer”?
table
=”CUSTOMERS”
>
??
-
<
property
?
/>
??
-
????????
<
component
?
name
=”homeAddress”?
class
=”Address”
>
??
-
????????????
<
parent
?
name
=”customer”
/>
??
-
????????????
<
property
/>
??
-
????????
</
component
>
??
-
????????
<
component
?
name
=”comAddress”?
class
=”Address”
>
??
-
????????????
<
parent
?
name
=”customer”
/>
??
-
????????????
<
property
/>
??
-
????????
</
component
>
??
-
????
</
class
>
??
-
</
hibernate-mapping
>
??
-
??
-
Public?class?Customer?implements?Serializable{ ??
-
????Address?homeAddress; ??
-
????Address?comAddress; ??
-
} ??
-
Public?class?Address?implements?Serializable{//是VO不是PO不能單獨(dú)Save,也不能關(guān)聯(lián)。 ??
-
????Customer?customer; ??
-
}??
3、映射聚合關(guān)系
代碼
-
<
set
/idbag?
name
=”images”?
table
=”IMAGES”?
lazy
=”true”
>
??
-
????
<
key
?
column
=”CUSTOMER_ID”
/>
??
-
????
<
composite-element
?
class
=”Image”
>
??
-
????????
<
parent
?
name
=”customer”
/>
??
-
????????
<
property
/>
??
-
<
property
/>
??
-
????
</
composite-element
>
??
-
</
set
/idbag
>
??
-
??
-
<
map
?
name
=”images”?
table
=”IMAGES”?
lazy
=”true”
>
??
-
????
<
key
?
column
=”CUSTOMER_ID”
/>
??
-
????
<
index
?
type
=”string”?
column
=”IMAGE_NAME”
/>
??
-
????
<
composite-element
?
class
=”Image”
>
??
-
????????
<
parent
?
name
=”customer”
/>
??
-
????????
<
property
/>
??
-
<
property
/>
??
-
????
</
composite-element
>
??
-
</
map
?
>
??
4、映射繼承關(guān)系
代碼
-
DOClass{ ??
-
???id ??
-
} ??
-
ClassA?extends?DOClass{ ??
-
????A1 ??
-
} ??
-
??
-
ClassC?extends?ClassA{ ??
-
????C1 ??
-
} ??
-
??
-
ClassD?extends?ClassA{ ??
-
????D1 ??
-
} ??
-
??
-
ClassG?extends?ClassD{ ??
-
????G1 ??
-
} ??
-
??
-
ClassH?extends?ClassD{ ??
-
????H1 ??
-
} ??
-
??
-
ClassB?extends?DOClass{ ??
-
????B1 ??
-
} ??
-
??
-
ClassE?extends?ClassB{ ??
-
????E1,e2,e3,e4,e5,e6 ??
-
} ??
-
??
-
ClassF?extends?ClassB{ ??
-
????F1,f2,f3,f4,f5,f6,f7 ??
-
} ??
-
??
-
TABLE_A?{ID(PK),A_TYPE(discriminator),A1,C1,D1,G1,H1} ??
-
TABLE_B?{ID(PK),B1} ??
-
TABLE_E?{B_ID(PK/FK),E1,E2,E3,E4,E5,E6} ??
-
TABLE_F?{B_ID(PK/FK),F(xiàn)1,F2,F3,F4,F5,F6,F7} ??
-
??
-
ClassA.hbm.xml ??
-
<
hibernate-mapping
>
??
-
????
<
class
?
name
=”ClassA”?
table
=”TABLE_A”?
discriminator-value
=”A”
>
??
-
????????
<
id
/>
??
-
????????
<
discriminator
?
column
=”A_TYPE”?
type
=”string”
/>
??
-
????????
<
property
?
name
=”a1”?
column
=”A1”
/>
??
-
????????
<
sub-class
?
name
=”ClassC”?
discriminator-value
=”C”
>
??
-
????????????
<
property
?
name
=”c1”?
column
=”C1”
/>
??
-
????????
</
sub-class
>
??
-
<
subclass
?
name
=”ClassD”?
discriminator-value
=”D”
>
??
-
????????????
<
property
?
name
=”d1”?
column
=”D1”
/>
??
-
????????????
<
subclass
?
name
=”ClassG”?
discriminator-value
=”G”
>
??
-
????????????????
<
property
?
name
=”g1”?
column
=”G1”
/>
??
-
????????????
</
subclass
>
??
-
????????????
<
subclass
?
name
=”ClassH”?
discriminator-value
=”H”
>
??
-
????????????????
<
property
?
name
=”h1”?
column
=”H1”
/>
??
-
????????????
</
subclasss
>
??
-
</
subclass
>
??
-
</
class
>
??
-
</
hibernate-mapping
>
??
-
ClassB.hbm.xml ??
-
<
hibernate-mapping
>
??
-
????
<
class
?
name
=”ClassB”?
table
=”TABLE_B”
>
??
-
????????
<
id
/>
??
-
????????
<
property
?
name
=”b1”?
column
=”B1”
/>
??
-
????????
<
joined-subclass
?
name
=”ClassE”?
table
=”TABLE_E”
>
??
-
????????????
<
key
?
column
=”B_ID”
/>
??
-
????????????
<
property
?
name
=”e1”?
column
=”E1”
/>
??
-
????????????
<
property
?
name
=”e2”?
column
=”E2”
/>
??
-
????????????
<
property
?
name
=”e3”?
column
=”E3”
/>
??
-
????????????
<
property
?
name
=”e4”?
column
=”E4”
/>
??
-
????????????
<
property
?
name
=”e5”?
column
=”E5”
/>
??
-
????????????
<
property
?
name
=”e6”?
column
=”E6”
/>
??
-
????????
</
joined-subclass
>
??
-
????????
<
joined-subclass
?
name
=”ClassF”?
table
=”TABLE_F”
>
??
-
????????????
<
key
?
column
=”B_ID”
/>
??
-
????????????
<
property
?
name
=”f1”?
column
=”F1”
/>
??
-
????????????
<
property
?
name
=”f2”?
column
=”F2”
/>
??
-
????????????
<
property
?
name
=”f3”?
column
=”F3”
/>
??
-
????????????
<
property
?
name
=”f4”?
column
=”F4”
/>
??
-
????????????
<
property
?
name
=”f5”?
column
=”F5”
/>
??
-
????????????
<
property
?
name
=”f6”?
column
=”F6”
/>
??
-
????????????
<
property
?
name
=”f7”?
column
=”F7”
/>
??
-
????????
</
joined-subclass
>
??
-
????
</
class
>
??
-
</
hibernate-mapping
>
??
5、映射Bag,List和Map
IDBag
代碼
-
IMAGES{ID(PK),CUSTOMER_ID(FK),F(xiàn)ILENAME} ??
-
List?
images
?=?
new
?ArrayList(); ??
-
Customer.hbm.xml ??
-
??
-
<
idbag
?
name
=”images”?
table
=”IMAGES”?
lazy
=”true”
>
??
-
????
<
collection-id
?
type
=”long”?
column
=”ID”
>
??
-
????????
<
generator
?
class
=”increment”
/>
??
-
????
</
collection-id
>
??
-
????
<
key
?
column
=”CUSTOMER_ID”
/>
??
-
????
<
element
?
column
=”FILENAME”?
type
=”string”?
not-null
=”true”
/>
??
-
</
idbag
>
??
List
代碼
-
IMAGES{CUSTOMER_ID(PK/FK),POSITION(PK),F(xiàn)ILENAME} ??
-
List?
images
?=?
new
?ArrayList(); ??
-
Customer.hbm.xml ??
-
<
list
?
name
=”images”?
table
=”IMAGES”?
lazy
=”true”
>
??
-
????
<
index
?
column
=”POSITION”
/>
??
-
????
<
key
?
column
=”CUSTOMER_ID”
/>
??
-
????
<
element
?
column
=”FILENAME”?
type
=”string”?
not-null
=”true”
/>
??
-
</
list
>
??
Map
代碼
-
IMAGES{CUSTOMER_ID(PK/FK),IMAGE_NAME(PK),F(xiàn)ILENAME} ??
-
Map?
images
?=?
new
?HashMap(); ??
-
<
map
?
name
=”images”?
table
=”IMAGES”?
lazy
=”true”
>
??
-
????
<
key
?
column
=”CUSTOMER_ID”
/>
??
-
<
index
?
column
=”IMAGE_NAME”?
type
=”string”
/>
??
-
????
<
element
?
column
=”FILENAME”?
type
=”string”?
not-null
=”true”
/>
??
-
</
map
>
??
-
??
-
Set?idbag?map?支持?jǐn)?shù)據(jù)庫(kù)排序??order?
by
?=”ID” ??
-
Set?map?支持內(nèi)存排序??
sort
?=?“MyComparator”??
6、映射一對(duì)一關(guān)聯(lián)關(guān)系特殊情況一
代碼
-
Public?
class
?Customer{ ??
-
????Address?homeAddress; ??
-
????Address?comAddress; ??
-
} ??
-
??
-
Customer.hbm.xml ??
-
<many-to-one?name=”homeAddress”?
class
=”Address”?column=”HOME_ADDRESS_ID”?cascade=”all”?unique=”
true
”/> ??
-
<many-to-one?name=”comAddress”?
class
=”Address”?column=”COM_ADDRESS_ID”?cascade=”all”?unique=”
true
”/> ??
-
??
-
Address.hbm.xml ??
-
<one-to-one?name=”address”?
class
=”Customer”?property-ref=”homeAddress”/>??
映射一對(duì)一關(guān)聯(lián)關(guān)系主鍵映射
代碼
-
Customer.hbm.xml ??
-
<
one-to-one
?
name
=”address”?
class
=”Address”?
cascade
=”all”
/>
??
-
Address.hbm.xml ??
-
<
class
?
name
=”address”
>
??
-
????
<
id
>
??
-
????????
<
generator
?
class
=”foreign”
>
??
-
????????????
<
param
?
name
=”property”
>
customer
</
param
>
??
-
????????
</
generator
>
??
-
????
</
id
>
??
-
<
one-to-one
?
name
=”customer”?
class
=”Customer”?
constrained
=”true”
/>
??
-
</
class
>
??
7、映射一對(duì)多關(guān)聯(lián)
代碼
-
<
class
?
name
=
"Person"
>
??
-
<
id
?
name
=
"id"
?
column
=
"personId"
>
??
-
????????
<
generator
?
class
=
"native"
/>
??
-
</
id
>
??
-
<
many-to-one
?
name
=
"address"
?
column
=
"addressId"
?
not-null
=
"true"
/>
??
-
</
class
>
??
-
??
-
<
class
?
name
=
"Address"
>
??
-
<
id
?
name
=
"id"
?
column
=
"addressId"
>
??
-
<
generator
?
class
=
"native"
/>
??
-
</
id
>
??
-
<
set
?
name
=
"people"
?
inverse
=
"true"
>
??
-
??????
<
key
?
column
=
"addressId"
/>
??
-
<
one-to-many
?
class
=
"Person"
/>
??
-
</
set
>
??
-
</
class
>
??
8、映射多對(duì)多關(guān)聯(lián)
代碼
-
<
set
?
name
=”items”?
table
=”CATEGORY_ITEM”?
lazy
=”true”?
cascade
=”save-update”
>
??
-
????
<
key
?
column
=”CATEGORY_ID”
>
??
-
????
<
many-to-many
?
class
=”Item”?
column
=”ITEM_ID”
/>
??
-
</
set
>
??
三、Inverse與cascade
Inverse
應(yīng)該將Set的inverse屬性設(shè)置為true,如果為many-to-many 需要將一方設(shè)置為true
如Customer:Order為1:N雙向關(guān)聯(lián),將Customer的Set的inverse設(shè)置為true,表示Customer與Order之間的關(guān)聯(lián)關(guān)系由Order端來(lái)維護(hù),如customer.getOrders().addOrder(o)不會(huì)更新Customer與Order之間的關(guān)聯(lián)關(guān)系,而order.setCustomer(o)才會(huì)更新Customer與Order之間的關(guān)聯(lián)關(guān)系。
Cascade
Save-update 保存、更新Customer會(huì)同步更新Order.
Delete 同步刪除
All 包含save-update和delete操作,另外調(diào)用當(dāng)前對(duì)象的evice或者lock時(shí),對(duì)關(guān)聯(lián)對(duì)象也調(diào)用相應(yīng)方法。
Delete-orphan 刪除所有和當(dāng)前對(duì)象解除關(guān)聯(lián)關(guān)系的對(duì)象。
All-delete-orphan 當(dāng)關(guān)聯(lián)雙方為父子關(guān)系是(父親控制孩子的持久化生命周期),如果父方刪除,子方自動(dòng)刪除(同delete),如果子方無(wú)父親,子方應(yīng)刪除。包含Delete和all-orphan的行為。
四、Hibernate緩存
Session 緩存(一級(jí)緩存),每一session確保自己的緩存的所有的持久對(duì)象唯一
通過(guò)調(diào)用session.setFlushMode()可設(shè)定緩存的清理模式,緩存的清理模式有三種:
FlushMode.AUTO:query、commit和flush的時(shí)候清理緩存。
FlushMode.COMMIT:commit和flush的時(shí)候清理緩存。
FlushMode.NEVER:只有在調(diào)用session.flush()的時(shí)候才清理緩存。
Session 只有在清理緩存的時(shí)候才會(huì)執(zhí)行相應(yīng)的sql操作。
可以使用session.evict()和session.clear()清空緩存。
Save、update、query都加入Session緩存
Select c.ID,c.Name,c.age,o.ORDER_NUM,o.CUSTOMER_ID from Customer c,inner join c.orders c 除外。
SessionFactory緩存(二級(jí)緩存)
代碼
-
<
class
?
name
=”Category”?
table
=”CATEGORYS”
>
??
-
????
<
cache
?
usage
=”read-write”
/>
??
-
????
<
id
/>
??
-
????
<
set
?
name
=”items”?
inverse
=”true”?
lazy
=”true”
>
??
-
????????
<
cache
?
usage
=”read-write”
/>
??
-
????????
<
key
…
/>
??
-
????
</
set
>
??
-
</
class
>
??
-
<
class
?
name
=”Item”
>
??
-
????
<
cache
?
usage
=”read-write”
/>
??
-
????
<
id
/>
??
-
</
class
>
??
-
??
-
Hibernate.cache.provider
=…………EhCacheProvider ??
-
Hibernate.cache.user_query_cache
=
true
??
-
??
-
Ehcache.xml ??
-
<
ehcache
>
??
-
????
<
diskStore
?
path
=”c:\\temp”
/>
??
-
????
<
defaultCache
??
-
????????
maxElementsInMemory
=”10000” ??
-
????????
eternal
=”false” ??
-
????????
timeToIdleSeconds
=”120” ??
-
????????
timeToLiveSeconds
=”120” ??
-
????????
overflowToDisk
=”true”
/>
??
-
????
<
cache
?
name
=”Category” ??
-
????????
maxElementsInMemory
=”10000” ??
-
????????
eternal
=”false” ??
-
????????
timeToIdleSeconds
=”120” ??
-
????????
timeToLiveSeconds
=”120” ??
-
????????
overflowToDisk
=”true”
/>
??
-
??
-
????
<
cache
?
name
=”Category.Items” ??
-
????????
maxElementsInMemory
=”10000” ??
-
????????
eternal
=”false” ??
-
????????
timeToIdleSeconds
=”120” ??
-
????????
timeToLiveSeconds
=”120” ??
-
????????
overflowToDisk
=”true”
/>
??
-
??
-
????
<
cache
?
name
=”Item” ??
-
????????
maxElementsInMemory
=”10000” ??
-
????????
eternal
=”false” ??
-
????????
timeToIdleSeconds
=”120” ??
-
????????
timeToLiveSeconds
=”120” ??
-
????????
overflowToDisk
=”true”
/>
??
-
??
-
????
<
cache
?
name
=”customerQueries”….
/>
?<!—設(shè)置查詢緩存? ??
-
??
-
</
ehcache
>
??
Query q = session.createQuery();
q.setCacheable(true);
q.setCacheRegion(“customerQueries”);
SessionFactory.evict(),SessionFactory.evictCollection()清除二級(jí)緩存。
直接調(diào)用JDBC API不會(huì)使用任何緩存。
二級(jí)緩存適合查詢較多但是很少更新的情況。
盡量對(duì)數(shù)據(jù)庫(kù)的所有操作由Hibernate來(lái)完成,而不要用其它方式對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作,否則可能與緩存沖突,當(dāng)然如果對(duì)緩存有深入研究的除外。
五、臨時(shí)對(duì)象(Transient Object)、持久對(duì)象(Persistent Object)和游離對(duì)象(Detached Object)
臨時(shí)對(duì)象:表示對(duì)象的主鍵不存在(OID不存在),Hibernate通過(guò)key的unsave-value或者version的unsaved-value來(lái)判斷是否為臨時(shí)對(duì)象。Session對(duì)臨時(shí)對(duì)象的唯一操作應(yīng)該是save()。
持久對(duì)象:在session緩存中存在持久對(duì)象,數(shù)據(jù)庫(kù)中存在相應(yīng)紀(jì)錄。
游離對(duì)象:數(shù)據(jù)庫(kù)中有相應(yīng)紀(jì)錄,session中不存在持久對(duì)象,可通過(guò)session.evict()獲得。
Session緩存中存在,數(shù)據(jù)庫(kù)中不存在,這是什么類型的對(duì)象?實(shí)際這種情況不存在,因?yàn)樗械腟ession操作均在事務(wù)中進(jìn)行,緩存中的數(shù)據(jù)是通過(guò)save、update或者query生成,而save或者update得到的是數(shù)據(jù)庫(kù)的獨(dú)占鎖,因此其它事務(wù)沒(méi)有可能刪除數(shù)據(jù)庫(kù)中的數(shù)據(jù)。而query獲得的是數(shù)據(jù)庫(kù)的共享鎖,因此其它事務(wù)也不可能獲得獨(dú)占鎖來(lái)更新數(shù)據(jù)。因此在一個(gè)事務(wù)內(nèi)部session緩存才有意義,如果脫離事務(wù),僅僅是只讀操作也可能導(dǎo)致session緩存中存在數(shù)據(jù)庫(kù)中根本不存在相應(yīng)紀(jì)錄的持久性對(duì)象。
六、Hibernate 的檢索策略
設(shè)定批量檢索數(shù)量 batch-size
外連接深度控制hibernate.max_fetch_depth
類級(jí)別檢索 load、get和find。其中l(wèi)oad可以設(shè)置延遲檢索(cglib生成代理類,可通過(guò)Hibernate.initialize()初始化),這也是load和get的區(qū)別之一。Get/find立即檢索,與是否設(shè)置延遲無(wú)關(guān)。
關(guān)聯(lián)檢索 立即檢索,延遲檢索,迫切左外連接檢索。Set/list/map等,無(wú)論是否延遲檢索得到的都是代理集合類。而非HashSet,ArrayList等。
Lazy與outer-joint
False,false 立即檢索
False,true 迫切左外連接,
True,false 延遲檢索
Many-to-one 的outer-join屬性
Auto:Customer的lazy為true則延遲加載,否則迫切左外連接
True:迫切左外連接
False:延遲加載或者立即加載由Customer的lazy決定。
One-to-one的延遲加載策略
<one-to-one name=”customer” class=”Customer” constrained=”true”/>
HQL會(huì)忽略迫切左外連接檢索和lazy(只有l(wèi)oad才為代理對(duì)象)策略。
Session.find(“from Customer c as c left join fetch c.orders where c.id=1”)
Hibernate的檢索方式
HQL、NativeSql和QBC
From Customer c inner join c.orders o 查詢結(jié)果保存到session緩存
Select c.ID,c.Name,c.age,o.ORDER_NUM,o.CUSTOMER_ID from Customer c,inner join c.orders c查詢結(jié)果不存入Session緩存。
七、Hibernate并發(fā)控制
樂(lè)觀鎖:VERSION或者timestamp控制,session.lock()立刻進(jìn)行版本檢查,session.update(),update的時(shí)候執(zhí)行版本檢查。
悲觀鎖:select for upload,session.get(Customer.class,new Long(1),LockMode.UPGRADE)
總結(jié):本文絕大多數(shù)為摘錄內(nèi)容,有些地方加上自己的理解,有不對(duì)之處懇請(qǐng)批評(píng)指正。看了書(shū),閱讀了相關(guān)帖子后,感覺(jué)學(xué)習(xí)Hibernate的重點(diǎn)應(yīng)該是Hibernate的緩存策、查詢和如何提高性能方面。
另外說(shuō)點(diǎn)自己的感受,本人做項(xiàng)目到現(xiàn)在都是在設(shè)計(jì)階段先有關(guān)系模型后有對(duì)象模型(其實(shí)一個(gè)Table一個(gè)對(duì)象),在這種情況下Hibernate的優(yōu)勢(shì)大大降低了,其實(shí)是為了Hibernate而Hibernate了,個(gè)人感覺(jué)在先有關(guān)系模型的情況下用Hibernate的意義不大。
如果按照OOAD的開(kāi)發(fā)流程先有對(duì)象模型,然后根據(jù)對(duì)象模型生成關(guān)系模型,那應(yīng)該說(shuō)用Hibernate用對(duì)了地方。畢竟Hibernate對(duì)繼承、多態(tài),各種復(fù)雜的關(guān)系都有很好的支持。
http://www.javaeye.com/topic/42854