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)將主從物件一併查出。