一對一關聯有三種情況: 一是關聯的實體都共享同樣的主鍵,二是其中一個實體通過外鍵關聯到另一個實體的主鍵 ,三是通過關聯表來保存兩個實體之間的連接關系。 接下來要介紹的是,注解形式的一對一單向外鍵關聯的情況。 環境 : 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 表參考的記錄,換句話說就是,在記錄關聯情況下,只有從表中的記錄先被刪除,主表中的記錄才有可能被刪除。
②一對一雙向外鍵關聯 : 如果,StudentCard 中的 Student 是必須的,也就是說,在 Student 中保存了一個 StudentCard 的引用,在 StudentCard 中也保存了一個 Student 的引用,
這就成了一對一的雙向關聯,StudentCard 的代碼修改成 :
實體 bean package net.yeah.fancydeepin.bidirectional.po;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table (name = "_studentcard" )
public class StudentCard implements Serializable{
private static final long serialVersionUID = 1L;
private Long id ;
private Date date ;
private Student student ;
@Id
public Long getId() {
return id ;
}
@Column (nullable = false )
@Temporal (TemporalType. DATE )
public Date getDate() {
return date ;
}
@OneToOne (mappedBy = "studentCard" )
public Student getStudent() {
return student ;
}
public void setId(Long id) {
this . id = id;
}
public void setDate(Date date) {
this . date = date;
}
public void setStudent(Student student) {
this . student = student;
}
}
mappedBy :
相當于 xml 方式配置的 inverse = "true",表示將表間關聯的這種關系的維護交給對方,對方才是關系的真正維護者,擁有主導權。
在雙向關聯中,有且僅有一端是作為主體( owner )端存在,主體端負責維護聯接列。對于不需要維護這種關系的從表則通過 mappedBy 屬性進行聲明。
最后不必也不能再在被關聯端(owned side)定義聯接列了,因為已經在主體端進行了聲明.
posted on 2012-10-13 13:20
fancydeepin 閱讀(7817)
評論(0) 編輯 收藏