1. 程式人生 > >hibernate關聯關係中的一對一以及懶載入的原理:lazy和fetch的理解

hibernate關聯關係中的一對一以及懶載入的原理:lazy和fetch的理解


********************

Person.java主對像

********************

package blog.hibernate.domain;

public class Person {
	private int id;
	private String name;
	private IdCard idCard;
	
	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 IdCard getIdCard() {
		return idCard;
	}
	public void setIdCard(IdCard idCard) {
		this.idCard = idCard;
	}
}

**************

Person.hbm.xml

**************

<?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 package="blog.hibernate.domain">
	<class name="Person" table="Person">
		<id name="id" column="PERSON_ID">
			<generator class="native"></generator>
		</id>
		<property name="name" column="PERSON_NAME"></property>
		<one-to-one name="idCard"></one-to-one>
	</class>
</hibernate-mapping>



********************

IdCard.java從物件

*******************

package blog.hibernate.domain;

public class IdCard {
	private int id;
	private String name;
	private Person person;
	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 Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
}

*************

IdCard.hbm.xml

*************

<?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 package="blog.hibernate.domain">
	<class name="IdCard" table="IDCARD">
		<id name="id" column="CARD_ID">
			<generator class="foreign">
				<param name="property">person</param>
			</generator>
		</id><!-- IdCard使用的主鍵來自主對像Person-->

                <property name="name" column="CARD_NAME" type="string"></property>
		<one-to-one name="person" constrained="true" ></one-to-one>
                <!-- constrained="true" 是為外來鍵加約束,這在一對一中只能從物件使用,主對像是不能使用的 -->
	</class>
</hibernate-mapping>



*****************

hibernate.cfg.xml

*****************

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/LazyOfOne2One</property><!-- ///表示連線本機的資料庫//localhost:3306 -->
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">1234</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
   		
        <property name="hibernate.hbm2ddl.auto">create</property>
        <property name="hibernate.show_sql">true</property>
                
        <mapping resource="blog/hibernate/domain/Person.hbm.xml"/>
        <mapping resource="blog/hibernate/domain/IdCard.hbm.xml"/>
    </session-factory>	
</hibernate-configuration>

******************

HibernateUtil.java

*******************

package blog.hibernate;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public final class HibernateUtil {
	
	private static SessionFactory sessionFactory;
	private HibernateUtil(){}
	
	static{
		Configuration cfg = new Configuration();
		sessionFactory =  cfg.configure("hibernate.cfg.xml").buildSessionFactory();
	}
	
	public static SessionFactory getSessionFactory(){
		return sessionFactory;
	}
	
	public static Session getSession(){
		return sessionFactory.openSession();
	}
	
	public static Object get(Class clazz,int Id) throws Exception {
		Session session = null;
		Object object = null;
		try {
			session = getSession();
			object = session.get(clazz, Id);
			//object = session.load(class, userId);
			return object;
		} catch (HibernateException e) {
			Logger.getLogger(HibernateUtil.class.getName()).log(Level.SEVERE, null, e);
			throw e;
		}finally{
			session.close();
		}
	}

	public static void add(Object object) throws Exception {
		Session session = null;
		Transaction tx = null;
		try {
			session = getSession();
			tx = session.beginTransaction();// 相當於tx =session.getTransaction();tx.begin();
			session.save(object);
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null) {
				tx.rollback();
			}
			throw e;
		} finally {
			if (session != null) {
				session.close();
			}
		}
	}
	
	public static void update(Object object) throws Exception {
		Session session = null;
		Transaction tx = null;
		try {
			session = getSession();
			tx = session.beginTransaction();// 相當於tx =session.getTransaction();tx.begin();
			session.update(object);
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null) {
				tx.rollback();
			}
			throw e;
		} finally {
			if (session != null) {
				session.close();
			}
		}
	}
	
	public static void delete(Object object) throws Exception {
		Session session = null;
		Transaction tx = null;
		try {
			session = getSession();
			tx = session.beginTransaction();// 相當於tx =session.getTransaction();tx.begin();
			session.delete(object);
			tx.commit();
		} catch (HibernateException e) {
			if (tx != null) {
				tx.rollback();
			}
			throw e;
		} finally {
			if (session != null) {
				session.close();
			}
		}
	}
}


*******************

junit test : One2One.java

*****************

package junit.test;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Session;
import org.junit.BeforeClass;
import org.junit.Test;

import blog.hibernate.HibernateUtil;
import blog.hibernate.domain.IdCard;
import blog.hibernate.domain.Person;

public class JuintTest {

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @Test
    public void test() {
        add();
        query1();
        query2();
        query3();
    }

    public void add() {
        IdCard idCard = new IdCard();
        idCard.setName("Lily");

        Person person = new Person();
        person.setName("Lily");

        idCard.setPerson(person);
//        必須通過idCard的setter去設定idCard與person的關聯,
//        而不能通過person的setter去設定他們的關聯
//        因為idCard的主鍵id是通過idCard的屬性person得到的

        try {
            HibernateUtil.add(person);
            HibernateUtil.add(idCard);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void query1() {
        Person person = null;
        try {
            person = (Person) HibernateUtil.get(Person.class, 1);
            System.out.println(person.getIdCard().getName());
        } catch (Exception ex) {
            Logger.getLogger(JuintTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void query2() {
        try {
            System.out.println("**********************");
            IdCard idCard = (IdCard) HibernateUtil.get(IdCard.class, 1);
            System.out.println(idCard.getPerson().getName());
        } catch (Exception ex) {
            Logger.getLogger(JuintTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("**********************");
    }

    public void query3() {
        Session session = null;
        try {
            session = HibernateUtil.getSession();
            IdCard idCard = (IdCard) session.get(IdCard.class, 1);
            System.out.println(idCard.getPerson().getName());
        } catch (Exception ex) {
            Logger.getLogger(JuintTest.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }
}


總結:

lazy 表示什時候抓取
fetch 表示用什麼方式抓取

一般來說
lazy 有兩種狀態 "proxy"(default,意思是使用懶載入,用的是代理) 和 "false" (意思是不使用懶載入)
fetch 有兩種狀態 "select"(default) 和 "join"

所以lazy 和 fetch 的組合一共有四種:

(a)、lazy = "proxy" (default) fetch = "select" (default)
(b)、lazy = "false"           fetch = "select" (default)
(c)、lazy = "proxy" (default) fetch = "join"
(d)、lazy = "false"           fetch = "join"

1、主對像:
查詢主對像與lazy 和 fetch 無關。
因為查詢主對像不使用懶載入,用關聯查詢(left outer join)將主從物件一併查出。

//查詢主對像

Hibernate:
    select
        person0_.PERSON_ID as PERSON1_0_1_,
        person0_.PERSON_NAME as PERSON2_0_1_,
        idcard1_.CARD_ID as CARD1_1_0_,
        idcard1_.CARD_NAME as CARD2_1_0_
    from
        Person person0_
    left outer join
        IDCARD idcard1_
            on person0_.PERSON_ID=idcard1_.CARD_ID
    where
        person0_.PERSON_ID=?
Lily

2、從物件:

列印輸出:

(a)、

lazy = "proxy"(default)    fetch = "select"(default)


**********************
//查詢從對像,通過從對像列印主物件的資訊(session關閉後再列印)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_0_,
        idcard0_.CARD_NAME as CARD2_1_0_
    from
        IDCARD idcard0_
    where
        idcard0_.CARD_ID=?
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at blog.hibernate.domain.Person_$$_javassist_0.getName(Person_$$_javassist_0.java)
    at junit.test.JuintTest.query(JuintTest.java:64)
    at junit.test.JuintTest.test(JuintTest.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:518)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1052)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:906)

**********************
//查詢從對像,通過從對像列印主物件的資訊(在同一個session裡)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_0_,
        idcard0_.CARD_NAME as CARD2_1_0_
    from
        IDCARD idcard0_
    where
        idcard0_.CARD_ID=?
Hibernate:
    select
        person0_.PERSON_ID as PERSON1_0_1_,
        person0_.PERSON_NAME as PERSON2_0_1_,
        idcard1_.CARD_ID as CARD1_1_0_,
        idcard1_.CARD_NAME as CARD2_1_0_
    from
        Person person0_
    left outer join
        IDCARD idcard1_
            on person0_.PERSON_ID=idcard1_.CARD_ID
    where
        person0_.PERSON_ID=?
Lily


(b)、

lazy = "false" fetch = "select"(default)


**********************
//查詢從對像,通過從對像列印主物件的資訊(session關閉後再列印)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_0_,
        idcard0_.CARD_NAME as CARD2_1_0_
    from
        IDCARD idcard0_
    where
        idcard0_.CARD_ID=?
Hibernate:
    select
        person0_.PERSON_ID as PERSON1_0_1_,
        person0_.PERSON_NAME as PERSON2_0_1_,
        idcard1_.CARD_ID as CARD1_1_0_,
        idcard1_.CARD_NAME as CARD2_1_0_
    from
        Person person0_
    left outer join
        IDCARD idcard1_
            on person0_.PERSON_ID=idcard1_.CARD_ID
    where
        person0_.PERSON_ID=?
**********************
//查詢從對像,通過從對像列印主物件的資訊(在同一個session裡)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_0_,
        idcard0_.CARD_NAME as CARD2_1_0_
    from
        IDCARD idcard0_
    where
        idcard0_.CARD_ID=?
Hibernate:
    select
        person0_.PERSON_ID as PERSON1_0_1_,
        person0_.PERSON_NAME as PERSON2_0_1_,
        idcard1_.CARD_ID as CARD1_1_0_,
        idcard1_.CARD_NAME as CARD2_1_0_
    from
        Person person0_
    left outer join
        IDCARD idcard1_
            on person0_.PERSON_ID=idcard1_.CARD_ID
    where
        person0_.PERSON_ID=?
Lily

(c)、

lazy = "proxy"(default)    fetch = "join"


**********************
//查詢從對像,通過從對像列印主物件的資訊(session關閉後再列印)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_1_,
        idcard0_.CARD_NAME as CARD2_1_1_,
        person1_.PERSON_ID as PERSON1_0_0_,
        person1_.PERSON_NAME as PERSON2_0_0_
    from
        IDCARD idcard0_
    inner join
        Person person1_
            on idcard0_.CARD_ID=person1_.PERSON_ID
    where
        idcard0_.CARD_ID=?
**********************
//查詢從對像,通過從對像列印主物件的資訊(在同一個session裡)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_1_,
        idcard0_.CARD_NAME as CARD2_1_1_,
        person1_.PERSON_ID as PERSON1_0_0_,
        person1_.PERSON_NAME as PERSON2_0_0_
    from
        IDCARD idcard0_
    inner join
        Person person1_
            on idcard0_.CARD_ID=person1_.PERSON_ID
    where
        idcard0_.CARD_ID=?
Lily


(d)、

lazy = "false"    fetch = "join"

**********************
//查詢從對像,通過從對像列印主物件的資訊(session關閉後再列印)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_1_,
        idcard0_.CARD_NAME as CARD2_1_1_,
        person1_.PERSON_ID as PERSON1_0_0_,
        person1_.PERSON_NAME as PERSON2_0_0_
    from
        IDCARD idcard0_
    inner join
        Person person1_
            on idcard0_.CARD_ID=person1_.PERSON_ID
    where
        idcard0_.CARD_ID=?
**********************
//查詢從對像,通過從對像列印主物件的資訊(在同一個session裡)
Hibernate:
    select
        idcard0_.CARD_ID as CARD1_1_1_,
        idcard0_.CARD_NAME as CARD2_1_1_,
        person1_.PERSON_ID as PERSON1_0_0_,
        person1_.PERSON_NAME as PERSON2_0_0_
    from
        IDCARD idcard0_
    inner join
        Person person1_
            on idcard0_.CARD_ID=person1_.PERSON_ID
    where
        idcard0_.CARD_ID=?
Lily

(a)、預設情況:
使用懶載入,查詢從物件只查詢IdCard一張表,要是要通過從物件訪問主對像則需要在同一個session裡去訪問,否則會報懶載入異常(報無法初始化代理--沒有session異常)。
當在同一個session裡通過從物件訪問主對像時會進行關聯查詢(left outer join),將主從物件一併查出來。
(b)、不使用懶載入,查詢從物件時直接查詢IdCard表,再通過關聯查詢(left outer join)將主從物件一併查出。
(c)、使用懶載入,但是fetch = "join"所以會使用內連線(inner join)將主從物件一併查出。
(d)、不使用懶載入,但是fetch = "join"所以會使用內連線(inner join)將主從物件一併查出。