一、雙向主鍵關(guān)聯(lián)
雙向的主鍵關(guān)聯(lián)其實(shí)是單向一對(duì)一主鍵關(guān)聯(lián)的一種特殊情況,只不過要在關(guān)聯(lián)對(duì)象的兩端的映射文件中都要進(jìn)行<one-to-one>的配置,另外還要在主映射的主鍵一端采用foreign外鍵關(guān)聯(lián)屬性。
這里同樣使用Person和IdCard來討論,一個(gè)人對(duì)應(yīng)著一個(gè)唯一的身份證,而且一個(gè)身份證也唯一映射著一個(gè)人,所以這就產(chǎn)生了雙向的關(guān)聯(lián)關(guān)系,Person的主鍵同樣也是IdCard的主鍵,分別是主鍵的同時(shí)也是外鍵,這種關(guān)聯(lián)關(guān)系成為雙向一對(duì)一映射,表現(xiàn)到關(guān)系模型中可如下圖:
圖中的兩個(gè)表采用了主鍵關(guān)聯(lián),person的主鍵是idCard的主鍵,所以它們之間構(gòu)成了朱外鍵的約束關(guān)系,并且保證唯一性,映射到對(duì)象模型中,轉(zhuǎn)變?yōu)閜erson類和idCard類的一對(duì)一關(guān)系,如下圖:
這種一對(duì)一的關(guān)系上篇文章中也有講到用的是<one-to-one>標(biāo)簽,另外這種一對(duì)一映射又是雙向的,所以要在兩個(gè)對(duì)象之間同時(shí)配置<one-to-one>,首先來看idCard對(duì)應(yīng)的類代碼和映射文件代碼。
1、IdCard對(duì)應(yīng)的信息
IdCard.java類,IdCard類和Person類之間有一對(duì)一的關(guān)聯(lián)關(guān)系所以要在IdCard類中添加對(duì)應(yīng)的Person屬性,這是為了能在映射文件中的外鍵中添加對(duì)應(yīng)的屬性,設(shè)置對(duì)應(yīng)的外鍵關(guān)聯(lián)類。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.src.hibernate; public class IdCard { //id屬性 private int id; public int getId() { return id; } public void setId( int id) { this .id = id; } //卡號(hào)屬性 private String cardNo; public String getCardNo() { return cardNo; } public void setCardNo(String cardNo) { this .cardNo = cardNo; } //卡號(hào)對(duì)應(yīng)的人 private Person person; public Person getPerson(){ return person; } public void setPerson(Person person){ this .person=person; } } |
IdCard.hbm.xml映射文件,在映射文件中添加外鍵屬性person,并添加對(duì)應(yīng)的<one-to-one>標(biāo)簽,目的是強(qiáng)制約束person類來實(shí)現(xiàn)一對(duì)一的映射關(guān)系,最后在映射中將constrained屬性設(shè)為true,保證強(qiáng)制約束關(guān)系。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-15 23:47:00 by Hibernate Tools 3.4.0.CR1 --> < hibernate-mapping > < class name = "com.src.hibernate.IdCard" table = "IDCARD" > < id name = "id" type = "int" column = "personId" > < generator class = "foreign" > < param name = "property" >person</ param > </ generator > </ id > < property name = "cardNo" type = "string" column = "cardno" ></ property > < one-to-one name = "person" constrained = "true" ></ one-to-one > </ class > </ hibernate-mapping > |
2、Person對(duì)應(yīng)的信息
Person.java類,在該類中除了添加基本的屬性外還要添加對(duì)應(yīng)的IdCard類作為屬性,因?yàn)樗鼈冎g是一對(duì)一的雙向關(guān)聯(lián)關(guān)系,所以在Person類中同樣要添加IdCard類,相同的道理IdCard類中同樣添加了Person類屬性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.src.hibernate; public class Person { //id號(hào) private int id; public int getId() { return id; } public void setId( int id) { this .id = id; } //姓名 private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } //idCard private IdCard idcard; public IdCard getIdcard() { return idcard; } public void setIdcard(IdCard idcard) { this .idcard = idcard; } } |
Person.hbm.xml映射文件,該文件中主鍵生成策略沒有特殊的要求,因?yàn)樗虸dCard類相互制約的關(guān)系,它的主鍵和外鍵都是IdCard的主鍵,另外因?yàn)槭且粚?duì)一關(guān)系所以要在映射文件中添加<one-to-one>標(biāo)簽來標(biāo)示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-15 23:47:00 by Hibernate Tools 3.4.0.CR1 --> < hibernate-mapping > < class name = "com.src.hibernate.Person" table = "PERSON" > < id name = "id" type = "int" column = "personId" > < generator class = "native" ></ generator > </ id > < property name = "name" type = "string" column = "personName" ></ property > <!-- one-to-one標(biāo)簽指示Hibernate如何加載其關(guān)聯(lián)對(duì)象,默認(rèn)根據(jù)主鍵加載,也就是拿到關(guān)系字段值,根據(jù)對(duì)端的主鍵來加載關(guān)聯(lián)對(duì)象 --> < one-to-one name = "idcard" ></ one-to-one > </ class > </ hibernate-mapping > |
3、Hibernate映射文件
上面的類和映射文件配置好后接下來要在Hibernate.cfg.xml中配置與數(shù)據(jù)庫映射的信息,需要將兩個(gè)配置文件添加到Hibernate配置文件中,這樣在生成對(duì)應(yīng)的數(shù)據(jù)庫時(shí)才能找到對(duì)應(yīng)的生成項(xiàng)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> < hibernate-configuration > < session-factory > < property name = "hibernate.connection.driver_class" >com.mysql.jdbc.Driver</ property > < property name = "hibernate.connection.url" >jdbc:mysql://localhost:3306/hibernate_one2one_pk1</ property > < property name = "hibernate.connection.username" >root</ property > < property name = "hibernate.connection.password" >1234</ property > < property name = "hibernate.dialect" >org.hibernate.dialect.MySQLDialect</ property > < mapping resource = "com/src/hibernate/Person.hbm.xml" /> < mapping resource = "com/src/hibernate/IdCard.hbm.xml" ></ mapping > </ session-factory > </ hibernate-configuration > |
4、生成結(jié)果
配置完成后就可以將上面的內(nèi)容生成對(duì)應(yīng)的數(shù)據(jù)庫了,在數(shù)據(jù)庫中它會(huì)按照配置的內(nèi)容生成相應(yīng)的表結(jié)構(gòu),在表中有相應(yīng)的外鍵和主鍵字段。生成表結(jié)構(gòu)時(shí)Hibernate會(huì)在控制臺(tái)輸出相應(yīng)的SQL語句,如下:
1
2
3
4
5
6
|
alter table IDCARD drop foreign key FK806F76ABAC038CD8 drop table if exists IDCARD drop table if exists PERSON create table IDCARD (personId integer not null , cardno varchar (255), primary key (personId)) create table PERSON (personId integer not null auto_increment, personName varchar (255), primary key (personId)) alter table IDCARD add index FK806F76ABAC038CD8 (personId), add constraint FK806F76ABAC038CD8 foreign key (personId) references PERSON (personId) |
生成的表結(jié)構(gòu)如下圖:
在兩張表中同時(shí)生成了personId主鍵,并且也是相應(yīng)的外鍵,它同時(shí)限制約束了兩張表的主鍵相同且唯一。
5、寫入加載測(cè)試
生成表后測(cè)試下對(duì)表的寫入和從表中讀取數(shù)據(jù),編寫相應(yīng)的測(cè)試類,測(cè)試采用的是單元測(cè)試,編寫對(duì)應(yīng)的測(cè)試方法。
5.1 寫入測(cè)試
在寫入到數(shù)據(jù)庫時(shí)一定要注意寫入的兩個(gè)對(duì)象都要轉(zhuǎn)化到對(duì)應(yīng)的Trainent狀態(tài),否則會(huì)出現(xiàn)狀態(tài)轉(zhuǎn)化的錯(cuò)誤,測(cè)試代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public void testSave1(){ Session session= null ; try { //創(chuàng)建一個(gè)會(huì)話對(duì)象 session=HibernateUtils.getSession(); //開啟會(huì)話事務(wù) session.beginTransaction(); //創(chuàng)建person對(duì)象,并保存 Person person= new Person(); person.setName( "zhangsan" ); session.save(person); //創(chuàng)建idCard對(duì)象,并保存 IdCard idcard= new IdCard(); idcard.setCardNo( "1111111111111" ); idcard.setPerson(person); session.save(idcard); //提交事務(wù),修改數(shù)據(jù)庫 session.getTransaction().commit(); } catch (Exception e){ //打印錯(cuò)誤信息 e.printStackTrace(); //業(yè)務(wù)回滾 session.getTransaction().rollback(); } finally { //關(guān)閉會(huì)話 HibernateUtils.closeSession(session); } } |
插入的數(shù)據(jù)如下圖:
5.2 加載測(cè)試
編寫加載方法,因?yàn)殛P(guān)聯(lián)關(guān)系是雙向的,所以相應(yīng)的加載操作應(yīng)該是通過一端加載另一端,也就是獲取對(duì)應(yīng)的Person類,并通過Person類來獲取對(duì)應(yīng)的IdCard信息,相反的也要成立,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public void testLoad1(){ Session session= null ; try { //創(chuàng)建一個(gè)會(huì)話對(duì)象 session=HibernateUtils.getSession(); //開啟會(huì)話事務(wù) session.beginTransaction(); //獲取person對(duì)象,并保存 Person person=(Person)session.load(Person. class , 5 ); System.out.println( "IdCard.Id: " +person.getIdcard().getId()); System.out.println( "IdCard.cardno: " +person.getIdcard().getCardNo()); //創(chuàng)建idCard對(duì)象,并保存 IdCard idcard=(IdCard)session.load(IdCard. class , 5 ); System.out.println( "Person.Id: " +idcard.getPerson().getId()); System.out.println( "Person.name: " +idcard.getPerson().getName()); //提交事務(wù),修改數(shù)據(jù)庫 session.getTransaction().commit(); } catch (Exception e){ //打印錯(cuò)誤信息 e.printStackTrace(); //業(yè)務(wù)回滾 session.getTransaction().rollback(); } finally { //關(guān)閉會(huì)話 HibernateUtils.closeSession(session); } } |
運(yùn)行上面的測(cè)試方法,在控制臺(tái)打印的相關(guān)內(nèi)容如下:
二、雙向外鍵關(guān)聯(lián)
雙向的外鍵關(guān)聯(lián)可以理解為外鍵關(guān)聯(lián)的一種特殊情況,這種特殊主要是由于它是一種雙向的對(duì)應(yīng)關(guān)系,在前篇文章中提到如果想要在一張表中添加一個(gè)外鍵字段的話可以使用<many-to-one>標(biāo)簽,它會(huì)關(guān)系模型中生成對(duì)應(yīng)的外鍵列。這里想要實(shí)現(xiàn)雙向的外鍵關(guān)聯(lián)就必須使用該標(biāo)簽。
1、對(duì)象模型
先來看對(duì)象模型,人和身份證屬于一對(duì)一的關(guān)系,一個(gè)人對(duì)應(yīng)著一個(gè)身份,所以它們之間的多重性是一對(duì)一的,并且這種對(duì)應(yīng)關(guān)系是雙向的。所以它的對(duì)象模型同雙向主鍵一對(duì)一是相同的,如下圖:
2、關(guān)系模型
對(duì)應(yīng)的關(guān)系模型會(huì)發(fā)生很大的變化,一對(duì)一的外鍵關(guān)聯(lián)關(guān)系會(huì)在一張表中生成對(duì)應(yīng)的外鍵,拿到人和身份證上來說也就是人的關(guān)系模型中會(huì)有一個(gè)身份證號(hào)的主鍵列,它們之間形成了雙向的一對(duì)一的情況,如下圖:
它們之間的對(duì)應(yīng)關(guān)系就是上圖中看到的,person表中有idCard表的主鍵,形成了一對(duì)一的外鍵關(guān)聯(lián)關(guān)系,而且是雙向的,也就是說通過person能夠獲取到idCard,另外通過idCard也能獲取到person。
Person對(duì)象和IdCard對(duì)象內(nèi)的代碼同上篇文章中的對(duì)象代碼一致,不在做代碼羅列,唯一不同的是映射文件中的配置問題。
3、映射文件
idCard.hbm.xml映射文件,idCard表不是映射的主表,所以在做一對(duì)一的映射時(shí)需要使用的是<one-to-one>標(biāo)簽來配置,并且需要制定person關(guān)系模型中的外鍵屬性,具體代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-18 22:27:43 by Hibernate Tools 3.4.0.CR1 --> < hibernate-mapping > < class name = "com.src.hibernate.IdCard" table = "IDCARD" > < id name = "id" type = "int" > < generator class = "native" /> </ id > < property name = "cardNo" type = "java.lang.String" > < column name = "CARDNO" /> </ property > < one-to-one name = "person" property-ref = "idCard" ></ one-to-one > </ class > </ hibernate-mapping > |
Person.hbm.xml映射文件,person表是映射的主表,需要在該表中添加一個(gè)外鍵屬性列來標(biāo)示idCard表,所以這里需要使用<many-to-one>標(biāo)簽,在person對(duì)象中生成相應(yīng)的外鍵,并且還要使用unique標(biāo)明屬性唯一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-18 22:27:43 by Hibernate Tools 3.4.0.CR1 --> < hibernate-mapping > < class name = "com.src.hibernate.Person" table = "PERSON" > < id name = "id" type = "int" column = "personId" > < generator class = "native" /> </ id > < property name = "name" type = "java.lang.String" > < column name = "NAME" /> </ property > < many-to-one name = "idCard" column = "idCardNo" unique = "true" not-null = "true" ></ many-to-one > </ class > </ hibernate-mapping > |
對(duì)象的映射文件配置完成,接下來生成關(guān)系模型,SQL語句如下:
1
2
3
4
5
6
|
alter table PERSON drop foreign key FK8C768F55794A52CA drop table if exists IDCARD drop table if exists PERSON create table IDCARD (id integer not null auto_increment, CARDNO varchar (255), primary key (id)) create table PERSON (personId integer not null auto_increment, NAME varchar (255), idCardNo integer not null unique , primary key (personId)) alter table PERSON add index FK8C768F55794A52CA (idCardNo), add constraint FK8C768F55794A52CA foreign key (idCardNo) references IDCARD (id) |
生成的SQL語句首先是創(chuàng)建的表,在建表時(shí)指定了主鍵列,創(chuàng)建完成后修改了兩個(gè)表指定外鍵屬性,形成一對(duì)一的關(guān)系。
編寫測(cè)試方法,采用單元測(cè)試,加載兩個(gè)類的對(duì)象,并分別從對(duì)象的一端獲取另一個(gè)對(duì)象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//加載對(duì)象,使用IdCard對(duì)象裝載person對(duì)象 public void testLoad1(){ Session session= null ; try { session=HibernateUtils.getSession(); session.beginTransaction(); //獲取IdCard對(duì)象,在IdCard中獲取與該對(duì)象唯一關(guān)聯(lián)的person對(duì)象 IdCard idcard=(IdCard)session.load(IdCard. class , 1 ); System.out.println( "person.Id= " +idcard.getPerson().getId()); System.out.println( "idCard.person.name= " +idcard.getPerson().getName()); //獲取Person對(duì)象,在Person對(duì)象中獲取與它唯一關(guān)聯(lián)的IdCard對(duì)象 Person person=(Person)session.load(Person. class , 1 ); System.out.println( "idCard.id: " +person.getIdCard().getId()); System.out.println( "idCard.cardNo: " +person.getIdCard().getCardNo()); //提交事務(wù) session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
生成的內(nèi)容:
對(duì)比兩種映射關(guān)系,主鍵和外鍵兩種映射,都是雙向的映射關(guān)系,需要在對(duì)象的兩端同時(shí)配置映射關(guān)系,不同的是主鍵只需要使用<one-to-one>因?yàn)樗恍枰蓪傩粤校潜仨殞?duì)表的主鍵采用foreign的主鍵生成策略,并標(biāo)示外鍵對(duì)象;外鍵的生成策略則需要采用<many-to-one>標(biāo)簽來生成新的外鍵列。
結(jié)語
雙向關(guān)聯(lián)中的一對(duì)一映射至此已經(jīng)討論完成,兩篇文章主要討論了雙向關(guān)聯(lián)中的兩種用法,其實(shí)還是很簡(jiǎn)單的,記住一句話想要生成外鍵就使用<many-to-one>標(biāo)簽,如果唯一那就添加unique屬性,<one-to-one>標(biāo)簽只是指明了一對(duì)一的關(guān)系它只是指明一個(gè)對(duì)象如何加載另一個(gè)對(duì)象并不在關(guān)系模型中添加新列。下篇文章將會(huì)對(duì)一對(duì)多關(guān)聯(lián)展開討論。