天天看點

Hibernate使用——一對一關聯示例

一對一關聯

一對一關聯包括如下的兩種類型:

  1. 主鍵關聯

  2. 唯一外鍵關聯

主鍵關聯 

       一對一的主鍵關聯形式,即兩張關聯表通過主鍵形成一對一映射關系。

       下面是一個例子,oham 陣型與lulu陣型配對進行精神單挑,要求兩組陣型人數相同,一對一PK:

Hibernate使用——一對一關聯示例

TOham.java

package learnHibernate.bean;

import java.io.Serializable;

public class TOham implements Serializable {
	private static final long serialVersionUID = -3474820872619575460L;
	
	private int id;
	private String name;
	private int groupId;
	private String meditation;
	
	private TLulu lu;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getGroupId() {
		return groupId;
	}
	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
	public TLulu getLu() {
		return lu;
	}
	public void setLu(TLulu lu) {
		this.lu = lu;
	}
	public String getMeditation() {
		return meditation;
	}
	public void setMeditation(String meditation) {
		this.meditation = meditation;
	}
}
           

TLulu.java

package learnHibernate.bean;

import java.io.Serializable;

public class TLulu implements Serializable {
	private static final long serialVersionUID = -252962688967803016L;
	
	private int id ;
	private String name;
	private String sixthSense;
	
	private TOham oh;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public TOham getOh() {
		return oh;
	}

	public void setOh(TOham oh) {
		this.oh = oh;
	}

	public String getSixthSense() {
		return sixthSense;
	}

	public void setSixthSense(String sixthSense) {
		this.sixthSense = sixthSense;
	}
}
           

 TOham.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  
<hibernate-mapping package="learnHibernate.bean">
	<class name="TOham" table="t_oham">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="native"/>
		</id>
		
		<property name="name" 
				  column="name" 
				  type="java.lang.String" />
				  
		<property name="meditation" 
				  column="meditation" 
				  type="java.lang.String" />
				  
		<property name="groupId" 
				  column="group_id" 
				  type="java.lang.Integer" />
				  
				  
		<!-- 通過one-to-one節點,将Toham類與TLulu類相關聯 -->		  
		<!-- cascade屬性,當主要方執行操作的時候,關聯對象是否執行同一操作,
		     如主要對象save-update-delete的時候,是否也同時對關聯對象做相同動作,
		     此處為“all”,表示對關聯類做同樣的動作 -->		  
		<one-to-one name="lu"
					class="TLulu"
					cascade="all"
					outer-join="true">
		</one-to-one>

	</class>
</hibernate-mapping>
      

TLulu.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  
<hibernate-mapping package="learnHibernate.bean">
	<class name="TLulu" table="t_lulu">
		<id name="id" column="id" type="java.lang.Integer">
		
			<!-- t_oham與t_lulu采用了主鍵關聯方式,是以關聯的記錄主鍵值必須保持同步 -->
			<!-- 是以隻需為一張表設定主鍵生成器,而另一張表的主鍵與之共享相同的主鍵值 -->
			<!-- 此處用foreign類型的主鍵生成器,表示t_lulu的主鍵id同時為外鍵,
			             通過property:oh的TOham類表明關聯的映射表為t_oham  -->
			<generator class="foreign">
				<param name="property">oh</param>
			</generator>
		</id>
		<!-- constrained為true,表明表t_oham的主鍵,被目前表t_lulu的外鍵關聯着 -->
		<one-to-one name="oh"
					class="TOham" 
					constrained="true"/>
		
		<property name="name" 
				  column="name" 
				  type="java.lang.String" />
				  
		<property name="sixthSense" 
				  column="sixthsense" 
				  type="java.lang.String" />
				  
	</class>
</hibernate-mapping>
      

執行代碼:

package learnHibernate;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;

import learnHibernate.bean.TLulu;
import learnHibernate.bean.TOham;
import learnHibernate.util.HibernateLocalUtil;

public class TestCase7 {

	@Test
	public void saveOrUpdateOhamlulu() {
		TOham oh = new TOham();
		oh.setName("Oham");
		oh.setMeditation("foresee");
		
		TLulu lu = new TLulu();
		lu.setName("Lulu");
		lu.setSixthSense("Perspective");
		
		//互相設定關聯,否則會抛
		//org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property
		oh.setLu(lu);
		lu.setOh(oh);
		
		SessionFactory factory = HibernateLocalUtil.getSessionFactory();
		Session session = factory.openSession();
		Transaction tx = session.beginTransaction();
		
		//由于TOham映射檔案中的one-to-one節點設定了cascade="all"
		//此處會被聯級儲存,若設定為"none",則隻儲存TOham
		session.save(oh);
		
		tx.commit();
		session.close();
		
	}
	
	//@Test
	public void getOhamlulu() {
		SessionFactory factory = HibernateLocalUtil.getSessionFactory();
		Session session = factory.openSession();
		
		TOham oh = (TOham)session.load(TOham.class, new Integer(9));
		
		//TLulu lu = (TLulu)session.load(TLulu.class, new Integer(9));
		
		//System.out.println(oh.getName());
		//System.out.println(lu.getName());
		session.close();
	}
	
	
}
           

  背景輸出sql:

Hibernate: 
    insert 
    into
        t_oham
        (name, meditation, group_id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        t_lulu
        (name, sixthsense, id) 
    values
        (?, ?, ?)
           

然後執行查詢代碼:

TOham oh = (TOham)session.load(TOham.class, new Integer(9));
		//因為TOham預設為延遲加載,調用對象時才會執行查詢資料庫
		System.out.println(oh.getName());
           

 背景輸出sql:

Hibernate: 
    select
        toham0_.id as id0_1_,
        toham0_.name as name0_1_,
        toham0_.meditation as meditation0_1_,
        toham0_.group_id as group4_0_1_,
        tlulu1_.id as id1_0_,
        tlulu1_.name as name1_0_,
        tlulu1_.sixthsense as sixthsense1_0_ 
    from
        t_oham toham0_ 
    left outer join
        t_lulu tlulu1_ 
            on toham0_.id=tlulu1_.id 
    where
        toham0_.id=?
           

注意:若将TOham.hbm.xml的outer-join設定為“false”,重新執行查詢代碼,結果:

 背景輸出sql:

Hibernate: 
    select
        toham0_.id as id0_0_,
        toham0_.name as name0_0_,
        toham0_.meditation as meditation0_0_,
        toham0_.group_id as group4_0_0_ 
    from
        t_oham toham0_ 
    where
        toham0_.id=?
Hibernate: 
    select
        tlulu0_.id as id1_0_,
        tlulu0_.name as name1_0_,
        tlulu0_.sixthsense as sixthsense1_0_ 
    from
        t_lulu tlulu0_ 
    where
        tlulu0_.id=?
           

這時,再把TLulu.hbm.xml的constrained設定為“false”,執行代碼,結果:

Hibernate: 
    select
        toham0_.id as id0_0_,
        toham0_.name as name0_0_,
        toham0_.meditation as meditation0_0_,
        toham0_.group_id as group4_0_0_ 
    from
        t_oham toham0_ 
    where
        toham0_.id=?
Hibernate: 
    select
        tlulu0_.id as id1_1_,
        tlulu0_.name as name1_1_,
        tlulu0_.sixthsense as sixthsense1_1_,
        toham1_.id as id0_0_,
        toham1_.name as name0_0_,
        toham1_.meditation as meditation0_0_,
        toham1_.group_id as group4_0_0_ 
    from
        t_lulu tlulu0_ 
    left outer join
        t_oham toham1_ 
            on tlulu0_.id=toham1_.id 
    where
        tlulu0_.id=?
           

 說實話,在下不太明白constrained的具體作用,隻知道當在采用主鍵關聯的方式去做一對一關聯時,從表(這裡的例子t_lulu)的映射配置裡id 的foreign類generator必須與constrained設定為true配對出現。

注意的一點是主從關系,我一般這樣了解,持有外鍵的表是從表,是關聯方,而被關聯的表是主表,是主要方,被關聯方,在hibernate的映射當中主從理清關系很重要,因為涉及級聯操作,弄不好會造成邏輯混亂。

唯一外鍵關聯

顧名思義,通過非主鍵的唯一外鍵來做一對一關聯,現在舉一例子:先前oham與lulu兩陣型心靈單挑進行中,可惜兩組總體水準相去甚遠,現在oham組要求組内每人可以召喚幫托,但為兼顧公平,現在隻允許每人召喚一個,并且隻能是cancan類型的,還有不能召喚同一人。。。

Hibernate使用——一對一關聯示例

 TOham.java:

package learnHibernate.bean;

import java.io.Serializable;

public class TOham implements Serializable {
	private static final long serialVersionUID = -3474820872619575460L;
	
	private int id;
	private String name;
	private int groupId;
	private String meditation;
	
	private TLulu lu;
	
	private TCancan can;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getGroupId() {
		return groupId;
	}
	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
	public TLulu getLu() {
		return lu;
	}
	public void setLu(TLulu lu) {
		this.lu = lu;
	}
	public String getMeditation() {
		return meditation;
	}
	public void setMeditation(String meditation) {
		this.meditation = meditation;
	}
	public TCancan getCan() {
		return can;
	}
	public void setCan(TCancan can) {
		this.can = can;
	}
}
           

TCancan.java:

package learnHibernate.bean;

public class TCancan {

	private int id;
	private String name;
	private String think;
	
	private TOham oh;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getThink() {
		return think;
	}
	public void setThink(String think) {
		this.think = think;
	}
	public TOham getOh() {
		return oh;
	}
	public void setOh(TOham oh) {
		this.oh = oh;
	}
}
           

 TOham.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  
<hibernate-mapping package="learnHibernate.bean">
	<class name="TOham" table="t_oham">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="native"/>
		</id>
		
		<property name="name" 
				  column="name" 
				  type="java.lang.String" />
				  
		<property name="meditation" 
				  column="meditation" 
				  type="java.lang.String" />
				  
		<property name="groupId" 
				  column="group_id" 
				  type="java.lang.Integer" />
				  
		<one-to-one name="lu"
					class="TLulu"
					cascade="all"
					outer-join="true">
		</one-to-one>
		
		<!-- 外鍵關聯,一般是多對一的,唯一外鍵關聯隻是其中一個特例,
		  	  是以此處用了many-to-one,并設定unqiue為true,表明此外鍵唯一 -->
		<many-to-one name="can" 
		             class="TCancan"
		             column="friend_id"
		             unique="true"/>

	</class>
</hibernate-mapping>
      

TCancan.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  
<hibernate-mapping package="learnHibernate.bean">
	<class name="TCancan" table="t_cancan">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="native" />
		</id>
		
		<!--<one-to-one name="oh"
					class="TOham"
					property-ref="can" />-->
		
		<property name="name" 
				  column="name" 
				  type="java.lang.String" />
				  
		<property name="think" 
				  column="think" 
				  type="java.lang.String" />
				  
	</class>
</hibernate-mapping>
      

執行代碼:

TOham oh = (TOham)session.load(TOham.class, new Integer(6));
		System.out.println(oh.getCan().getName());
           

結果:

Hibernate: 
    select
        toham0_.id as id0_1_,
        toham0_.name as name0_1_,
        toham0_.meditation as meditation0_1_,
        toham0_.group_id as group4_0_1_,
        toham0_.friend_id as friend5_0_1_,
        tlulu1_.id as id1_0_,
        tlulu1_.name as name1_0_,
        tlulu1_.sixthsense as sixthsense1_0_ 
    from
        t_oham toham0_ 
    left outer join
        t_lulu tlulu1_ 
            on toham0_.id=tlulu1_.id 
    where
        toham0_.id=?
Hibernate: 
    select
        tcancan0_.id as id2_0_,
        tcancan0_.name as name2_0_,
        tcancan0_.think as think2_0_ 
    from
        t_cancan tcancan0_ 
    where
        tcancan0_.id=?
           

 此處要注意的是因為先前的例子我們做了主鍵一對一關聯,是以此處left join了t_lulu表,若是用單純的唯一外鍵映射例子做實驗,此處的sql可能是left outer join的,可以去試試,因為many-to-one的outer-join預設為true。這是hibernate底層優化的結果。

另外 注意若不調用oh下面的can對象的話hibernate不會去執行查詢t_canca的操作。

此處在hibernate的映射層面是個單向的關聯,也就是說,執行下面代碼:

TCancan can = (TCancan)session.load(TCancan.class, new Integer(1));
		System.out.println(can.getOh());
           

 結果是hibernate隻會查詢t_cancan表,不會去查t_oham表:

Hibernate: 
    select
        tcancan0_.id as id2_0_,
        tcancan0_.name as name2_0_,
        tcancan0_.think as think2_0_ 
    from
        t_cancan tcancan0_ 
    where
        tcancan0_.id=?
           

若我們把TCancan.hbm.xml中的one-to-one節點的注釋放開:

<one-to-one name="oh"
					class="TOham"
					property-ref="can" />      

 其中property-ref指定為TOham的屬性名為can,意思是TOham的can就是關聯着本類TCancan的。這樣就實作了映射層面的雙向關聯。

在執行代碼:

TCancan can = (TCancan)session.load(TCancan.class, new Integer(1));
		System.out.println(can.getOh());
           

 結果把t_cancan關聯進來了:

Hibernate: 
    select
        tcancan0_.id as id2_2_,
        tcancan0_.name as name2_2_,
        tcancan0_.think as think2_2_,
        toham1_.id as id0_0_,
        toham1_.name as name0_0_,
        toham1_.meditation as meditation0_0_,
        toham1_.group_id as group4_0_0_,
        toham1_.friend_id as friend5_0_0_,
        tlulu2_.id as id1_1_,
        tlulu2_.name as name1_1_,
        tlulu2_.sixthsense as sixthsense1_1_ 
    from
        t_cancan tcancan0_ 
    left outer join
        t_oham toham1_ 
            on tcancan0_.id=toham1_.friend_id 
    left outer join
        t_lulu tlulu2_ 
            on toham1_.id=tlulu2_.id 
    where
        tcancan0_.id=?
           

此處就引申了一個問題,若一個實體的映射關聯多了,就不能總讓其join來join去了,那麼就要考慮如何設定映射配置,使得性能最優化。。。