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

俗話說,本身寫的代碼,6個月後也是別人的代碼……複習!複習!複習!涉及的知識點總結以下:java

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

 

  自動生成數據庫腳本web

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

  要生成數據庫表,得先建好實體類,而後創建對應的配置文件(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)。api

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

 

  一對一的單向外鍵關係架構

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

  註解方式:實體類和配置以下:框架

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

下面是學生類:dom

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類之間成爲一對一關聯關係。不然就是組件映射關係。

 

  問題小結:

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

  1. 忘記導入dom4j包
  2. 忘記導入hibernate註解須要用到的ejb3-persistence.jar(其他的兩個是hibernate-annotations.jar,hibernate-commons-annotations.jar包)。
  3. org.hibernate.MappingException: Unknown entity: xxxx異常:
    1. 多是由於使用註解的時候沒有導入正確的包。Entity包是javax.persistence.Entity;而不是hibernate包下的annotation。
    2. 使用註解時沒有在*.cfg.xml下配置<mapping class=""/>,從而形成org.hibernate.MappingException: Unknown entity異常的
  4. java.lang.NoClassDefFoundError: javax/persistence/Cacheable異常: javax.persistence.CacheableJPA 2.0 規範中的東西,須要導入jar包:hibernate-jpa-2.0-api-1.0.1.Final.jar
  5. 關於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便可。
  6. 千萬注意hibernate主配置文件裏必須引入mapping——實體關係映射
  7. 注意區分註解和配置文件的實體映射關係在主配置文件的寫法。
  8. 聯合主鍵類必須實現序列化接口和重寫equals和hashCode方法。
  9. Specified key was too long; max key length is 1024 bytes異常,這個是MySQL5.2.0以及以前的bug,升級數據庫或者減小聯合主鍵的字段長度就能解決,當MySQL數據庫的字符集爲latin1時,是沒有問題的,若是用UTF8字符集,就會報這個錯誤,由於在設置聯合主鍵時,若是採用UTF-8編碼,那麼只能存儲1024/3=300多個單位長度,因此長度不能超過300.
相關文章
相關標籤/搜索