hibernate annotation 之 一對一單向外鍵關聯 – JAVA編程語言程序開發技術文章

一對一關聯有三種情況:
 
一是關聯的實體都共享同樣的主鍵,二是其中一個實體通過外鍵關聯到另一個實體的主鍵 ,三是通過關聯表來保存兩個實體之間的連接關系。

接下來要介紹的是,註解形式的一對一單向外鍵關聯的情況。

環境 :  JDK 1.6,eclipse 3.6,maven 3.0.4,hibernate 3.3.2,junit 4.7,mysql 5.1

 pom.xml 核心部分清單

<dependencies>

 

   <!– Hibernate framework –>

    <dependency>

      <groupId>org.hibernate</groupId>

      <artifactId>hibernate-core</artifactId>

      <version>3.3.2.GA</version>

    </dependency>

    <!– Hibernate Dependency Start –>

    <dependency>

      <groupId>cglib</groupId>

      <artifactId>cglib</artifactId>

      <version>2.2</version>

    </dependency>

    <dependency>   

      <groupId>javassist</groupId>   

      <artifactId>javassist</artifactId>   

      <version>3.9.0.GA</version>

    </dependency>

    <dependency>

      <groupId>org.hibernate</groupId>

      <artifactId>hibernate-annotations</artifactId>

      <version>3.4.0.GA</version>

    </dependency>

    <dependency>   

      <groupId>org.slf4j</groupId>   

      <artifactId>slf4j-log4j12</artifactId>   

      <version>1.5.8</version>

    </dependency>

    <!– Hibernate Dependency End –>

    <!– mysql driver –>

    <dependency>

      <groupId>mysql</groupId>

      <artifactId>mysql-connector-java</artifactId>

      <version>5.1.17</version>

    </dependency>

    <!– junit –>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.7</version>

      <scope>test</scope>

    </dependency>

 

 </dependencies>

 

註 : 此處配置 pom.xml 是使用 maven 來管理 jar 包,如果你沒有使用 maven,則需手動導入相關 jar 包。

 實體 bean

package net.yeah.fancydeepin.unidirectional.po;

import java.io.Serializable;

import javax.persistence.CascadeType;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.FetchType;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

import javax.persistence.OneToOne;

import org.hibernate.annotations.GenericGenerator;

 

@Entity

public class Student implements Serializable{

   private static final long serialVersionUID = 1L;

 

   private String id;
   private String name;
   private StudentCard studentCard;

 

   @Id

   @GenericGenerator(name = "idGenerator", strategy = "uuid")

   @GeneratedValue(generator = "idGenerator")

   @Column(length = 32)

   public String getId() {

      return id;

   }

   @Column(length = 18, nullable = false)

   public String getName() {

      return name;

   }

   @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

   public StudentCard getStudentCard() {

      return studentCard;

   }

   public void setId(String id) {

      this.id = id;

   }

   public void setName(String name) {

      this.name = name;

   }

   public void setStudentCard(StudentCard studentCard) {

      this.studentCard = studentCard;

   }

}

@OneToOne         建立實體 bean 之間的一對一的關聯

cascade                  級聯策略,即,當對主對象做某種操作時,是否對其相關聯的子對象也做相對應的操作。它有5個值可選,分別是 :

             CascadeType.PERSIST : 級聯新建

             CascadeType.REMOVE  :  級聯刪除

             CascadeType.REFRESH : 級聯刷新

             CascadeType.MERGE   : 級聯更新

             CascadeType.ALL     : 囊括以上四項

fetch                        抓取策略,它有2個值可選,分別是 :

             FetchType.LAZY   :   延遲抓取

             FetchType.EAGER  :   立即抓取

Tips :       延遲抓取數據能夠保證應用隻有在需要的時候才去數據庫抓取相應的數據記錄,這樣能夠避免過多,

             或過早的加載數據庫表中的數據,從而減少應用內存的開銷。

@JoinColumn     該註解與@Column 註解用法有點相似,可以通過name來指定聯接列的名稱,如果沒有該註解沒有被聲明,

             默認的聯接列名稱是 : 關聯的類的短類名(首字母小寫,不帶包名)_id。

 實體 bean

package net.yeah.fancydeepin.unidirectional.po;

import java.io.Serializable;

import java.util.Date;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Temporal;

import javax.persistence.TemporalType;

@Entity

public class StudentCard implements Serializable{

   private static final long serialVersionUID = 1L;

   private Long id;

   private Date date;

   @Id

   public Long getId() {

      return id;

   }

   @Column(nullable = false)

   @Temporal(TemporalType.DATE)

   public Date getDate() {

      return date;

   }

   public void setId(Long id) {

      this.id = id;

   }

   public void setDate(Date date) {

      this.date = date;

   }

}

                                 

 hibernate.cfg.xml 清單
 
<hibernate-configuration>

    <session-factory>

        <!– Database connection settings –>

        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

        <property name="connection.url">jdbc:mysql://localhost:3306/temp</property>

        <property name="connection.username">username</property>

        <property name="connection.password">password</property>

        <!– SQL dialect –>

        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!– Enable Hibernate's automatic session context management –>

        <property name="current_session_context_class">thread</property>

        <!– Echo all executed SQL to stdout –>

        <property name="show_sql">true</property>

        <property name="format_sql">true</property>

        <!– OneToOne 單向 –>

        <mapping class="net.yeah.fancydeepin.unidirectional.po.Student" />

        <mapping class="net.yeah.fancydeepin.unidirectional.po.StudentCard" />

        <!– OneToOne 雙向 –>

        <!–

        <mapping class="net.yeah.fancydeepin.bidirectional.po.Student" />

        <mapping class="net.yeah.fancydeepin.bidirectional.po.StudentCard" />

        –>

    </session-factory>

</hibernate-configuration>

 

 Junit Test

package junit.test;

import java.util.Date;

import net.yeah.fancydeepin.unidirectional.po.Student;

import net.yeah.fancydeepin.unidirectional.po.StudentCard;

import org.hibernate.Session;

import org.hibernate.cfg.AnnotationConfiguration;

import org.hibernate.tool.hbm2ddl.SchemaExport;

import org.junit.BeforeClass;

import org.junit.Test;

public class TestApp {

   private static Session session;

   private static final String ID = "402881f13a5480c2013a5480c3d00001";

   @BeforeClass

   public static void beforeClass(){

      session = new AnnotationConfiguration().configure().buildSessionFactory().getCurrentSession();

   }

   @Test

   public void createTable(){

      new SchemaExport(new AnnotationConfiguration().configure()).create(true, true);

   }

   @Test

   public void insert(){

      Student student = new Student();

      student.setName("fancy");

      StudentCard studentCard = new StudentCard();

      studentCard.setId(3110005981L);

      studentCard.setDate(new Date());

      student.setStudentCard(studentCard);

      session.beginTransaction();

      session.save(student);

      session.getTransaction().commit();

   }

   @Test

   public void query(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

      System.out.println(student.getName());

      //StudentCard studentCard = student.getStudentCard();
 

      //System.out.println(studentCard.getDate());

   }

   @Test

   public void update(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

      student.setName("fancydeepin");

   // StudentCard studentCard = student.getStudentCard();

   // studentCard.setDate(new Date());

   // student.setStudentCard(studentCard);

      session.update(student);

      session.getTransaction().commit();

   }

   @Test

   public void delete(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

   // StudentCard studentCard = student.getStudentCard();

   // session.delete(studentCard);

      session.delete(student);

      session.getTransaction().commit();

   }

}

1. 建表

    在 Junit 測試類中執行建表方法 createTable,數據庫中生成表結構 :

   

   

2. 插入數據 ( 級聯插入 )

    在 Junit 測試類中執行 insert 方法,後臺發出兩條插入的 SQL 語句,數據庫中產生的數據 :

       

    在這裡,student 是主表,studentcard 是從表,Student 類級聯 ( CascadeType.ALL ) 瞭 StudentCard 類,當 Student 的實例對象被持久化時,

    若 Student 對象的 StudentCard 實例對象不為 null,則該 StudentCard 對象也將被持久化到數據庫,若為 null,則不會被持久化。

3. 查詢數據 ( 延遲加載 )

    在 Junit 測試類中執行 query 方法,後臺發出 Student 的 select SQL 語句 :
   
    Hibernate:
        select
            student0_.id as id0_0_,
            student0_.name as name0_0_,
            student0_.studentCard_id as studentC3_0_0_
        from
            Student student0_
        where
            student0_.id=?

    若將 query 方法裡被註釋的行去掉,後臺除瞭會發出 Student 的 select SQL 語句之外,還會發出 StudentCard 的 select SQL :

    Hibernate:
        select
            student0_.id as id0_0_,
            student0_.name as name0_0_,
            student0_.studentCard_id as studentC3_0_0_
        from
            Student student0_
        where
            student0_.id=?
    Hibernate:
        select
            studentcar0_.id as id1_0_,
            studentcar0_.date as date1_0_
        from
            StudentCard studentcar0_
        where
            studentcar0_.id=?

4. 更新數據 ( 級聯更新 )
 
    在 Junit 測試類中執行 update 方法,後臺發出 Student 的 update SQL 語句 :

    Hibernate:
        update
            Student
        set
            name=?,
            studentCard_id=?
        where
            id=?

    若將 update 方法中的註釋行去掉,後臺除瞭會發出 Student 的 update SQL 語句之外,還會發出 StudentCard 的 update SQL :

    Hibernate:
        update
            Student
        set
            name=?,
            studentCard_id=?
        where
            id=?
    Hibernate:
        update
            StudentCard
        set
            date=?
        where
            id=?

    註 :  隻有當 Student 對象的屬性值發生變化時,才會發出 Student 的 update SQL,如果 Student 對象中的屬性值沒有發生過改變,

            則不會發出 Student 的 update SQL ; StudentCard 也是一樣的。

5. 刪除數據 ( 級聯刪除 )

    在 Junit 測試類中執行 delete 方法,後臺發出 Student 和 StudentCard 的 delete SQL 語句 :

    Hibernate:
        delete
        from
            Student
        where
            id=?
    Hibernate:
        delete
        from
            StudentCard
        where
            id=?

    由於是 CascadeType.ALL 的級聯策略,當從表中的記錄被刪除時,主表中被關聯的記錄也將會被刪除掉。

    若是將 delete 方法中的註釋行去掉,將最後註釋行的下一行註釋掉,也就是如果將 session.delete(student); 這行 註釋起來的話,
 
    後臺將拋出   org.hibernate.ObjectDeletedException 的異常,這是由於從表 student 關聯瞭主表 studentcard,因此不能直接去刪除

    studentcard 表中被 student 表參考的記錄。

發佈留言