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)将主从对象一并查出。