程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Hibernate(5)—— 聯合主鍵 、一對一關聯關系映射(xml和注解) 和 領域驅動設計,hibernatexml

Hibernate(5)—— 聯合主鍵 、一對一關聯關系映射(xml和注解) 和 領域驅動設計,hibernatexml

編輯:JAVA綜合教程

Hibernate(5)—— 聯合主鍵 、一對一關聯關系映射(xml和注解) 和 領域驅動設計,hibernatexml


俗話說,自己寫的代碼,6個月後也是別人的代碼……復習!復習!復習!涉及的知識點總結如下:

  • One to One 映射關系
    • 一對一單向外鍵(XML/Annotation)
    • 一對一雙向外鍵關聯(XML/Annotation)
    • 聯合主鍵
    • 一對一單向外鍵聯合主鍵(Xml/Annotation)
    • 一對一組件關聯(XML/Annotation)
    • 理解組件
  • 領域驅動設計——自動生成數據庫腳本
  • 一對一關系的小結
  • 一些出錯問題的總結

 

  自動生成數據庫腳本

  一般在項目開發過程中,我們的習慣是先建好數據庫和表,然後在進行開發,而hibernate作為一款ORM架構模式的實現框架,我們要好好利用,可以利用hibernate反向工程生成*.hbm.xml文件跟POJO類,個人認為由於目前所使用的數據庫大部分還都是關系數據庫,而hibernate把對數據庫的操作都對象化了,更應當從對象出發,生成數據庫裡面相關表,這樣更加符合人認知事物的習慣,hibernate3就已經提供了自帶的工具hbm2ddl,能夠通過對象建立數據庫腳本。

  要生成數據庫表,得先建好實體類,然後建立對應的配置文件(or 注解),最後和主配置文件hibernate.cfg.xml關聯即可。作用的代碼如下:

SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure()); // 生成,並輸出sql到文件(當前目錄)和數據庫, 創建表結構,第一個true 表示在控制台打印sql語句,第二個true 表示導入sql語句到數據庫 se.create(true, true); View Code

  這就是領域驅動設計/開發(DDD,Domain Driven Design/Development)。

  系統的設計應該基於對象模型,考慮對象的設計和邏輯,然後按照對象模型建立數據庫關系模型,這才是現在面向對象開發的步驟,不是說先設計數據庫,然後再設計對象。當在設計時,我們的領域模型需要改變,只需修改Hibernate的結構和應用程序,不需要修改數據庫架構,只要利用SchemaExport工具重新生成數據庫架構就ok。

 

  一對一的單向外鍵關系

  比如一個人對應一張身份證,反過來一張身份證必須對應一個人,不存在歧義。一個學生類,一個身份證類,學生唯一對應一張身份證,反過來,身份證唯一表示一個學生。這就是一對一的關系,其中學生能持有身份證,故學生表裡的身份證的id就是學生表的外鍵,學生id是學生表的主鍵。這也叫一對一單向外鍵關系。

  注解方式:實體類和配置如下:

import org.hibernate.annotations.GenericGenerator; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; // 身份證類 @Entity public class IdCard { private String pid;// 身份證號碼 private String province;// 省份 @Id @GeneratedValue(generator="pid")// 聲明一個主鍵生成策略, 需要用到下面的hibernate的主鍵生成策略,叫pid @GenericGenerator(name="pid",strategy="assigned") // 聲明一個主鍵生成策略,身份證需要手工指定,設置主鍵生成器的名字,必須是pid // @GeneratedValue的作用是JPA的默認實現自定義主鍵生成策略,@GenericGenerator是hibernate在JPA的基礎上增強。 public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } } View Code

下面是學生類:

import javax.persistence.*; // 學生實體類 @Entity public class Students { private int sid; // 學生編號 private String sname; private IdCard cardId; // 身份證 @OneToOne(cascade = CascadeType.ALL) // 設置學生表的外鍵:一對一單向外鍵(Annotation),設置為全部級聯更新 @JoinColumn(name = "pid",unique=true) // 在主表中加入外鍵pid,唯一的 public IdCard getCardId() { return cardId; } public void setCardId(IdCard cardId) { this.cardId = cardId; } @Id @GeneratedValue(strategy = GenerationType.AUTO) // 學生類的主鍵因為是整型,故設置自動增長 //@GeneratedValue // 不寫其實也ok、相當於上面的效果,也就是默認是auto(MySQL的native類型) public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } } View Code

最後反向生成數據庫表。注意:一對一的關系,必須先保存外鍵的對象,再保存主鍵的對象,完整的CRUD的代碼如下

import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.classic.Session; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestStudentsByAnno{ private static final Logger LOG = LoggerFactory.getLogger(TestStudentsByAnno.class); private static SessionFactory sessionFactory; @Before public void setUp() { LOG.info("setUp"); AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration().configure(); sessionFactory = annotationConfiguration.buildSessionFactory(); testSchemaExport(annotationConfiguration); } @After public void tearDown() { LOG.info("tearDown"); sessionFactory.close(); } @Test public void testSave() { Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); try { IdCard c = new IdCard(); c.setPid("66666666666"); c.setProvince("shanxi"); Students s = new Students(); s.setSname("zhangsan"); s.setCardId(c); //先保存身份證 session.save(c); //再保存學生 session.save(s); tx.commit(); } catch(Exception ex) { ex.printStackTrace(); tx.rollback(); } } private void testSchemaExport(AnnotationConfiguration annotationConfiguration) { LOG.info("=============================testSchemaExport"); SchemaExport se = new SchemaExport(annotationConfiguration); // 生成,並輸出sql到文件(當前目錄)和數據庫, 創建表結構,第一個true 表示在控制台打印sql語句,第二個true 表示導入sql語句到數據庫 se.create(true, true); } } View Code

Hibernate: insert into IdCard (province, pid) values (?, ?)
Hibernate: insert into Students (pid, sname) values (?, ?)

 

  XML配置方式:注意主控方的配置文件裡需要加上<many-to-one name="cardId" column="pid" unique="true"/>,主控方就是持有唯一外鍵的那個表,name=外鍵id,保持唯一,因為一對一,必須設置為unique為true!

身份證的配置:

<hibernate-mapping> <class name="net.nw.vo.fk.IdCard" table="idcard"> <id name="pid" column="pid" type="string"> <generator class="assigned"/> </id> <property name="province" column="province" type="string"/> </class> </hibernate-mapping> View Code

學生的配置:作為主控方,需要加上一對一的關系映射屬性,使用的是many-to-one標簽

<hibernate-mapping> <class name="net.nw.vo.fk.Students" table="students"> <id name="sid" column="sid" type="int"> <generator class="native"/> </id> <property name="sname" column="sname" type="string"/> <many-to-one name="cardId" column="pid" unique="true"/> </class> </hibernate-mapping> View Code

還有主配置文件的關聯,下面是測試代碼:

public void testSave() { Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); try { IdCard c = new IdCard(); c.setPid("1111111111"); c.setProvince("dadadada"); Students s = new Students(); s.setSname("daaaaaaa"); s.setCardId(c); //先保存身份證 session.save(c); //再保存學生 session.save(s); tx.commit(); } catch(Exception ex) { ex.printStackTrace(); tx.rollback(); } } View Code

成功插入了數據

 

  一對一雙向外鍵關聯

  很簡單,可以理解為雙方都持有對方的引用,也就是你中有我,我中有你的一對一關聯關系,比如學生表包含身份證id,同時身份證表也包含學生id,同時互為對方的外鍵。

  注解方式:實體類和配置如下:

  兩個實體類其中之一必須使用注解:@OneToOne(mappedBy=“實體類的引用”) ,屬性mappedBy是必須的,目的是設置將控制權交給另外一方(實體類)。雙向的關聯必須設置mappedBy屬性。也就是說雙向關聯只能交給一方去控制,不可能在雙方都設置外鍵保存關聯關系,否則都無法保存。因為學生表包含身份證id,同時身份證表也包含學生id,互為對方的外鍵。如果互為主控方,那麼先保存誰呢?如果要保存學生,則應該先保存身份證,而身份證也是主控方,要保存身份證必須先保存學生……死循環發生。

1 import javax.persistence.*; 2 3 //學生實體類 4 @Entity 5 public class Students { 6 private int sid; //編號 7 8 private String sname; //姓名 9 10 private IdCard cardId; // 學生持有身份證的引用,因為學生表關聯了身份證表 11 12 // cascade屬性需要設置為all,表明刪除student時同時刪除對應的身份證 13 @OneToOne(cascade = CascadeType.ALL) // 本例子裡學生作為主控方,則需要在身份證類那兒聲明mappedBy屬性 14 @JoinColumn(name="pid",unique=true) // 學生表的外鍵是身份證id:pid,唯一的 15 public IdCard getCardId() { 16 return cardId; 17 } 18 19 public void setCardId(IdCard cardId) { 20 this.cardId = cardId; 21 } 22 23 @Id 24 @GeneratedValue 25 // 默認 等價於 @GeneratedValue(strategy=GenerationType.AUTO) 26 public int getSid() { 27 return sid; 28 } 29 30 public void setSid(int sid) { 31 this.sid = sid; 32 } 33 34 public String getSname() { 35 return sname; 36 } 37 38 public void setSname(String sname) { 39 this.sname = sname; 40 } 41 } View Code

  本例子裡學生表(實體類)作為主控方,那麼本類不需要設置@OneToOne(mappedBy=“”),而是需要在另一方——身份證類裡聲明@OneToOne(mappedBy=“”)屬性

1 import org.hibernate.annotations.GenericGenerator; 2 import javax.persistence.*; 3 4 //身份證類 5 @Entity 6 public class IdCard { 7 private String pid;//身份證號碼 8 9 private String province;//省份 10 11 private Students stu;//反過來身份證表也關聯學生表,對應程序也就是身份證類包含學生對象的引用 12 13 // 身份證把控制權交給學生控制,mappedBy值是主控方持有的引用(外鍵) 14 // 記住:只要雙向關聯,必須雙方有一方要指定mappedBy屬性,不能互相指定,目的是避免死循環發生 15 @OneToOne(mappedBy="cardId") 16 public Students getStu() { 17 return stu; 18 } 19 20 public void setStu(Students stu) { 21 this.stu = stu; 22 } 23 24 @Id 25 @GeneratedValue(generator="pid") 26 @GenericGenerator(name="pid",strategy="assigned") 27 public String getPid() { 28 return pid; 29 } 30 31 public void setPid(String pid) { 32 this.pid = pid; 33 } 34 35 public String getProvince() { 36 return province; 37 } 38 39 public void setProvince(String province) { 40 this.province = province; 41 } 42 } View Code

  測試生成數據庫腳本:

alter table Students drop foreign key FK73AC29B8917F52BE drop table if exists IdCard drop table if exists Students create table IdCard (pid varchar(255) not null, province varchar(255), primary key (pid)) create table Students (sid integer not null auto_increment, sname varchar(255), pid varchar(255) unique, primary key (sid)) alter table Students add index FK73AC29B8917F52BE (pid), add constraint FK73AC29B8917F52BE foreign key (pid) references IdCard (pid) View Code

  我發現其實和剛剛一對一單向外鍵關聯的表結構一樣,好像沒什麼區別啊?其實關鍵問題是所謂的一對一雙向關聯,就是實體類裡雖然彼此包含了對方的引用,但是在實際設計數據庫時的關鍵問題是——把控制權交給誰?上例把IdCard的stu交給了學生類去控制,如果反過來,學生類stu的身份證pid交給身份證去控制,看代碼:

1 import org.hibernate.annotations.GenericGenerator; 2 import javax.persistence.*; 3 4 //身份證類 5 @Entity 6 public class IdCard { 7 private String pid;//身份證號碼 8 9 private String province;//省份 10 11 private Students stu;//反過來身份證表也關聯學生表,對應程序也就是身份證類包含學生對象的引用 12 13 @OneToOne(cascade=CascadeType.ALL) // 現在身份證是主控方 14 @JoinColumn(name="sid",unique=true) 15 public Students getStu() { 16 return stu; 17 } 18 19 public void setStu(Students stu) { 20 this.stu = stu; 21 } 22 23 @Id 24 @GeneratedValue(generator="pid") 25 @GenericGenerator(name="pid",strategy="assigned") 26 public String getPid() { 27 return pid; 28 } 29 30 public void setPid(String pid) { 31 this.pid = pid; 32 } 33 34 public String getProvince() { 35 return province; 36 } 37 38 public void setProvince(String province) { 39 this.province = province; 40 } 41 } View Code

  主控方交給身份證

1 import javax.persistence.*; 2 3 //學生實體類 4 @Entity 5 public class Students { 6 private int sid; //編號 7 8 private String sname; //姓名 9 10 private IdCard cardId; // 學生持有身份證,學生表關聯了身份證表 11 12 @OneToOne(mappedBy="stu") // 學生持有的身份證把控制權交給身份證控制 13 public IdCard getCardId() { 14 return cardId; 15 } 16 17 public void setCardId(IdCard cardId) { 18 this.cardId = cardId; 19 } 20 21 @Id 22 @GeneratedValue 23 // 默認 等價於 @GeneratedValue(strategy=GenerationType.AUTO) 24 public int getSid() { 25 return sid; 26 } 27 28 public void setSid(int sid) { 29 this.sid = sid; 30 } 31 32 public String getSname() { 33 return sname; 34 } 35 36 public void setSname(String sname) { 37 this.sname = sname; 38 } 39 } View Code

  測試數據庫腳本,發現和剛剛不一樣了,身份證裡有外鍵sid,而學生表沒有了之前的外鍵pid,說明身份證去控制學生了。

create table IdCard (pid varchar(255) not null, province varchar(255), sid integer unique, primary key (pid)) create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid)) View Code

  注意:千萬別讓雙方都持有對方的引用作為外鍵,這樣是沒有意義的。雖然語法不會出錯,執行起來肯定會出死循環的問題。繼續測試,現在是身份證控制學生,那麼先保存學生(學生id是外鍵),在保存身份證:

1 import org.hibernate.Session; 2 import org.hibernate.SessionFactory; 3 import org.hibernate.Transaction; 4 import org.hibernate.cfg.AnnotationConfiguration; 5 import org.hibernate.cfg.Configuration; 6 import org.hibernate.tool.hbm2ddl.SchemaExport; 7 import org.junit.After; 8 import org.junit.Before; 9 import org.junit.Ignore; 10 import org.junit.Test; 11 12 public class TestStudentsByAnno { 13 private static SessionFactory sessionFactory; 14 15 @Before 16 public void setUp() throws Exception { 17 System.out.println("setUp()..."); 18 sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); 19 } 20 21 @After 22 public void tearDown() throws Exception { 23 System.out.println("tearDown()..."); 24 sessionFactory.close(); 25 } 26 27 @Test 28 29 public void testSave() { 30 Session session = sessionFactory.getCurrentSession(); 31 Transaction tx = session.beginTransaction(); 32 33 try { 34 Students s = new Students(); 35 s.setSname("xiaoli"); 36 IdCard c = new IdCard(); 37 c.setPid("12345656788"); 38 c.setProvince("hebei"); 39 c.setStu(s); 40 //先保存學生 41 session.save(s); 42 //再保存身份證 43 session.save(c); 44 45 tx.commit(); 46 } catch(Exception ex) { 47 ex.printStackTrace(); 48 tx.rollback(); 49 } 50 } 51 52 @Test 53 @Ignore 54 public void testSchemaExport() { 55 SchemaExport se = new SchemaExport(new Configuration().configure()); 56 se.create(true, true); 57 } 58 } View Code

結果:

 

  XML配置方式:實體類和配置如下:

  大同小異,理解了注解的方式,xml就是書寫的注意,主控方還是和之前一對一單向管理的寫法一樣:<many-to-one name="cardId" column="pid" unique="true"/>,被控方需要注意下,要寫成<one-to-one name="stu" property-ref="cardId"/>,下面實現一個學生為主控方的例子:

<?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> <class name="net.nw.vo.bfk.Students" table="students"> <id name="sid" column="sid" type="int"> <generator class="native"/> </id> <property name="sname" column="sname" type="string"/> <!-- 主控方,外鍵是身份證pid --> <many-to-one name="cardId" column="pid" unique="true"/> </class> </hibernate-mapping> ----------------------------------------------------------------------------- <?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> <class name="net.nw.vo.bfk.IdCard" table="idcard"> <id name="pid" column="pid" type="string"> <generator class="assigned"/> </id> <property name="province" column="province" type="string"/> <!-- 身份證是被控的,也需要配置,使用one-to-one屬性 property-ref意思是把控制交給學生類,讓學生使用cardId這個身份證的引用去控制 --> <one-to-one name="stu" property-ref="cardId"/> </class> </hibernate-mapping> View Code

  實體類不變,還是互相持有對方的引用,然後測試也是注意,先保存外鍵的實體類身份證,最後保存學生類:

IdCard c = new IdCard(); c.setPid("999999999"); c.setProvince("beijing"); Students s = new Students(); s.setSname("wangwu"); s.setCardId(c); //先保存身份證 session.save(c); //再保存學生 session.save(s); tx.commit(); View Code

 

  什麼是聯合主鍵?

  聯合主鍵就是用多個字段組成的主鍵。用這個主鍵包含的字段作為主鍵,這個組合在數據表中是唯一,且加了主鍵索引。比如,訂單表裡有很多字段,一般情況只要有個訂單號做主鍵就可以,但是現在可能會有補充訂單,使用相同的訂單號,那麼這時單獨使用訂單號就不可以,因為會有重復。那麼可以再使用個訂單序列號作為區別。把訂單號和訂單序列號設成聯合主鍵。即使訂單號相同,訂單序列號不同也是可以唯一的。

 

  一對一單向外鍵聯合主鍵關聯

  之前的主鍵都是一個字段,比如身份證的主鍵pid,現在在之前的身份證id上加一個血型字段作為聯合主鍵,那麼需要新建一個聯合主鍵類,代碼如下:

  注解方式:實體類和配置如下:

  先創建聯合主鍵類,須實現serializable接口,進行序列化,還必須重寫hashCode()和equals()方法。聯合主鍵類的注解是@Embeddable,使能嵌入的意思,而實體類的主鍵使用@EmbeddedId標識,代碼如下:

import javax.persistence.Embeddable; import java.io.Serializable; //聯合主鍵類的注解是 Xxxxable 類型,嵌入的意思 @Embeddable public class IdCardPK implements Serializable{ /** * 必須實現序列化接口 */ private static final long serialVersionUID = 1L; private String pid;//身份證號碼,主鍵字段 private String bloodType;//新加的字段:血型,也作為主鍵字段 public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getBloodType() { return bloodType; } public void setBloodType(String bloodType) { this.bloodType = bloodType; } /** * 必須重寫equals和hashCode * * @param obj Object * @return boolean */ @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public int hashCode() { return super.hashCode(); } } View Code

  再創建對應的實體類:

1 import javax.persistence.EmbeddedId; 2 import javax.persistence.Entity; 3 import javax.persistence.OneToOne; 4 5 //身份證實體類 6 @Entity 7 public class IdCard { 8 private IdCardPK pk;// 身份證的主鍵是聯合主鍵 IdCardPK pk 9 10 private String province;// 省份 11 12 private Students stu; // 身份證持有學生類的引用 13 14 // 把控制權交給學生 15 @OneToOne(mappedBy="cardId")//只要是雙向關聯,就一定要指定mappedBy 16 public Students getStu() { 17 return stu; 18 } 19 20 public void setStu(Students stu) { 21 this.stu = stu; 22 } 23 24 // 聯合主鍵的主鍵形式是Xxxxid類型,具體的主鍵設置在聯合主鍵類裡進行 25 @EmbeddedId 26 public IdCardPK getPk() { 27 return pk; 28 } 29 30 public void setPk(IdCardPK pk) { 31 this.pk = pk; 32 } 33 34 public String getProvince() { 35 return province; 36 } 37 38 public void setProvince(String province) { 39 this.province = province; 40 } 41 } 42 43 import javax.persistence.CascadeType; 44 import javax.persistence.Entity; 45 import javax.persistence.GeneratedValue; 46 import javax.persistence.Id; 47 import javax.persistence.JoinColumn; 48 import javax.persistence.JoinColumns; 49 import javax.persistence.OneToOne; 50 51 //學生實體類 52 @Entity 53 public class Students { 54 private int sid; //編號 55 56 private String sname; //姓名 57 58 private IdCard cardId; // 學生的外鍵,學生控制身份證 59 60 // 主控類的外鍵設置比較復雜點,因為有多個主鍵字段了 61 @OneToOne(cascade=CascadeType.ALL) 62 @JoinColumns( 63 { 64 // referencedColumnName設置對應數據庫的字段名字,name是類的字段名字 65 @JoinColumn(name="pid",referencedColumnName="pid"), 66 @JoinColumn(name="bloodType",referencedColumnName="bloodtype") 67 } 68 ) 69 public IdCard getCardId() { 70 return cardId; 71 } 72 73 public void setCardId(IdCard cardId) { 74 this.cardId = cardId; 75 } 76 77 @Id 78 @GeneratedValue 79 public int getSid() { 80 return sid; 81 } 82 83 public void setSid(int sid) { 84 this.sid = sid; 85 } 86 87 public String getSname() { 88 return sname; 89 } 90 91 public void setSname(String sname) { 92 this.sname = sname; 93 } 94 } View Code

  注意千萬別忘了配置hibernate主文件:

<mapping class="net.nw.vo.ufk.Students" /> <mapping class="net.nw.vo.ufk.IdCard" /> <mapping class="net.nw.vo.ufk.IdCardPK" /> View Code

  生成的數據庫腳本:

create table IdCard (bloodType varchar(255) not null, pid varchar(255) not null, province varchar(255), primary key (bloodType, pid))
create table Students (sid integer not null auto_increment, sname varchar(255), bloodType varchar(255), pid varchar(255), primary key (sid))

  同樣保存數據也是先保存受控類,在保存主控類。

 

  XML配置方式:實體類和配置如下:

   理解了上述主鍵的意思,xml也就很簡單了:

  被控方:

<composite-id name="pk" class="聯合主鍵的類"> <key-property name="pid" column="pid" type="string"/> <key-property name="bloodType" type="string"/> <generator class="assigned"></generator> </composite-id> View Code

  主控方:

<many-to-one name="cardId"> <column name="pid" unique="true"/> <column name="bloodid"/> </many-to-one> View Code

注意,聯合主鍵類不需要單獨的映射文件進行配置!代碼如下:

<?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> <class name="net.nw.vo.ufk.IdCard" table="idcard"> <!-- 聯合主鍵的配置 --> <composite-id name="pk" class="net.nw.vo.ufk.IdCardPK"> <key-property name="pid" column="pid" type="string"/> <key-property name="bloodType" type="string"/> <generator class="assigned"/> </composite-id> <property name="province" column="province" type="string"/> </class> </hibernate-mapping> ------------------------------------------------------------------ <?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> <!-- 主控方--> <class name="net.nw.vo.ufk.Students" table="students"> <id name="sid" column="sid" type="int"> <generator class="native"/> </id> <property name="sname" column="sname" type="string"/> <!-- 主控方的外鍵 --> <many-to-one name="cardId"> <!-- 該外鍵的表又包含聯合主鍵 --> <column name="pid" unique="true"/> <column name="bloodid"/> </many-to-one> </class> </hibernate-mapping> View Code

 

  一對一組件關聯映射

  組件類就是一個POJO類,什麼主鍵或者映射配置都不需要!因為實際上就是只有一張表,是最簡單的,先不說理論,看例子:

  注解方式:實體類和配置如下:

  IdCard裡什麼主鍵都不寫,當然配置也不需要,就是一個POJO類

//身份證類,就是一個POJO類 public class IdCard { private String pid;//身份證號碼 private String province;//省份 public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } } View Code

  只有被組件注入的實體類需要配置,它的主鍵注解為@Embedded

import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; //學生實體類 @Entity public class Students { private int sid; //編號 private String sname; //姓名 private IdCard cardId; @Id @GeneratedValue public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } @Embedded public IdCard getCardId() { return cardId; } public void setCardId(IdCard cardId) { this.cardId = cardId; } } View Code

  執行數據庫腳本:

drop table if exists Students
create table Students (sid integer not null auto_increment, pid varchar(255), province varchar(255), sname varchar(255), primary key (sid))

  執行插入測試:

IdCard c = new IdCard(); c.setPid("1234567"); c.setProvince("hebei"); Students s = new Students(); s.setSname("a"); s.setCardId(c); session.save(s); tx.commit(); View Code

  

  開始扯理論:如何理解組件?
  組件是某個實體類的邏輯組成部分,這個組件與實體的本質區別在於組件沒有oid(參考前面的例子,它沒有任何配置),故可以理解為能把組件當做值對象。例子中把省份和身份證id單獨提取抽象出來作為了一個組件類,這個類就叫做值對象,也就是所說的組件。

  采用組件映射的優點

  實現了對象細粒度的劃分,層次更加分明,復用率高。上面的student類,分為了基本信息sid、sname,還將身份證分離了出來,此外,還可以將愛好,家庭成員等信息再作為一類分離出來,進行細粒度的劃分。分離出來的組件,也可以作為其他對象(例如teacher、employer等)的組件,這樣就為復用提供了方便。

 

  XML配置方式:實體類和配置如下:

  映射關系文件: 

<component name="cardId" class="net.nw.vo.component.IdCard"> <property name="pid" column="pid" type="string"></property> <property name="province" type="string"></property> </component> View Code

映射文件中,通過<component>標簽,將組件類的屬性映射到學生表中,這樣,student表中的字段就會是Student類中的sid、sname以及組件類中的pid和provincel。而在數據庫中,身份證類不用單獨創建一張表。代碼如下:

<?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> <class name="net.nw.vo.bfk.Students" table="students"> <id name="sid" column="sid" type="int"> <generator class="native"/> </id> <property name="sname" column="sname" type="string"/> <component name="cardId" class="net.nw.vo.component.IdCard"> <property name="pid" column="pid" type="string"></property> <property name="province" type="string"></property> </component> </class> </hibernate-mapping> View Code

 

  一對一映射關系小結:

  再看一個例子,比如web開發中最最常見的用戶表,裡面有聯系方式和姓名:

  實體細粒度模型

public class Tusers { private Integer id; private Name name; // 1.姓名 private Contact contact; // 2.聯系方式 } public class Name { private String firstname; private String lastname; } public class Contact { private String address; private String email; private String telphone; } View Code

  粗粒度的數據庫模型

  實體一對一關聯:假如數據庫中已經存在contact、name表,並且Contact、Name設計成實體類,則Tusers 類與Contact,Name類之間成為一對一關聯關系。否則就是組件映射關系。

 

  問題小結:

  期間使用注解的時候出現了一些瑕疵:

  • java.lang.NoClassDefFoundError: javax/persistence/Cacheable異常: javax.persistence.Cacheable 是 JPA 2.0 規范中的東西,需要導入jar包:hibernate-jpa-2.0-api-1.0.1.Final.jar
  • 關於java.lang.NoSuchMethodError: javax.persistence.OneToOne.orphanRemoval()Z異常:JUnit報Caused by: java.lang.NoSuchMethodError: javax.persistence.OneToOne.orphanRemoval()Z,原因是ejb3-persistence.jar和hibernate-jpa-2.0-api-1.0.1.Final.jar有沖突,刪掉ejb3-persistence.jar即可。
  • 千萬注意hibernate主配置文件裡必須引入mapping——實體關系映射
  • 注意區分注解和配置文件的實體映射關系在主配置文件的寫法。
  • 聯合主鍵類必須實現序列化接口和重寫equals和hashCode方法。
  • Specified key was too long; max key length is 1024 bytes異常,這個是MySQL5.2.0以及之前的bug,升級數據庫或者減少聯合主鍵的字段長度就能解決,當MySQL數據庫的字符集為latin1時,是沒有問題的,如果用UTF8字符集,就會報這個錯誤,因為在設置聯合主鍵時,如果采用UTF-8編碼,那麼只能存儲1024/3=300多個單位長度,所以長度不能超過300.
  •  

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved