hibernate筆記: 關於懶載入和load()方法
重點牢記
1.Load支援懶載入,get不支援懶載入。
2.lazy的生命週期與session相同,lazy載入必須依賴於session一直開啟。
3.Hibernate lazy屬性,在3.x後是預設開啟的,在以前版本中預設是關閉的。
4.hibernate通過cjlib實現代理。
5.load方法加載出來的是代理物件。
6.可以利用Hibernate.initialize(emp)來初始化代理物件達到命中資料庫
7.代理類在未命中資料庫之前,他只有真實物件的ID屬性,其他資料都是沒有的
Empoyee.hbm.xml的配置
- <hibernate-mapping
- package
- <class name="Employee" table="employees" discriminator-value="0" >
- <id name="id">
- <generator class="increment"/>
- </id>
- <property name="name" not-null="true"/>
- <many-to-one name="department" column="department_id" ></many-to-one
- </class>
- </hibernate-mapping>
Department.hbm.xml的配置
- <hibernate-mapping
- package="hiber.domain">
- <class name="Department" table="departments" discriminator-value="0" >
- <id name="id">
- <generator class="increment"/>
- </
- <property name="name" not-null="true"/>
- </class>
- </hibernate-mapping>
測試(一):
- public class Many2One {
- /**
- * @param args
- */
- public static void main(String[] args) {
- add();
- Employee employee =query();
- System.out.println(employee.getDepartment().getName());//(1) 進行訪問
- }
- /**
- * 新增一個Department 和Employee 以及它們之間的關聯
- */
- static void add(){
- Session s =null;
- Transaction t = null;
- s=HiberUtil.getSession();
- t=s.beginTransaction();
- Department department = new Department();
- department.setName("人事部");
- Employee employee = new Employee();
- employee.setName("steve");
- employee.setDepartment(department);
- s.save(department);
- s.save(employee);
- t.commit();
- s.close();
- }
- /**
- * 使用load方法測試 懶載入的相關問題
- * @return
- */
- static Employee query(){
- Session s =null;
- Transaction t = null;
- s=HiberUtil.getSession();
- t=s.beginTransaction();
- Employee employee = (Employee)s.load(Employee.class, 1);
- t.commit();
- s.close();
- return employee;
- }
- }
結果: 會報錯
- Hibernate: select max(id) from departments
- Hibernate: select max(id) from employees
- Hibernate: insert into departments (name, id) values (?, ?)
- Hibernate: insert into employees (name, department_id, id) values (?, ?, ?)
- Exception in thread "main" 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 hiber.domain.Employee_ _javassist_0.getDepartment(Employee_ _javassist_0.java)
- at hiber.sth.Many2One.main(Many2One.java:17)
結果分析:
當使用session中的load方法查詢資料庫中的記錄時,我們返回的是一個代理物件,而不是真正需要的那個物件;例如資料庫中存有個Employee表,我們有cn.binyulan.doman.Employee的領域物件,如果查詢Id值為“200626313”的Employee employee = (Employee)session.load(Employee.class,"200626313"),然後我們列印System.out.println(employee.getClass);得到的結果為cn.binyulan.domain.Employee._$$_jvst793_0,名字很奇怪吧,這個物件是Hibernate幫我們生成的,從名字可以看出它是對Employee類的增強類的物件,其實這就是個代理物件,這個物件裡並沒有我們需要的Employee的資料,所以如果你在session關閉後在使用employee來獲取資訊,如除了獲得ID以外的employee.getName();就會出現如下異常:org.hibernate.LazyInitializationException: could not initialize proxy - no Session,這就說明了employee中沒有我們需要的資料了。
emp物件現在到底是什麼呢,它其實是一個代理類,這個類具有查詢資料庫的能力,當session沒有關閉的時候如果我們呼叫emp.getName等方法;那麼這個類會去查詢資料庫並返回相應的資料。之後我們關閉session之後再去使用這個類就不會有異常了,可是我們如果呼叫emp.getName()只是為了讓代理類去查資料庫,如果別人看我們的程式碼時候覺得這兩句在邏輯上根本就沒有用,別人就很容易註釋掉,一註釋掉就又會出現異常了,所以hibernate提供了一個方法Hibernate.initialize(emp);這樣就可以初始化這個代理物件了。
不知道大家注意到沒有,上面的那句程式碼Employee emp = (Employee)session.load(Employee.class,1)把emp強制轉化為Employee型別,有人會問:不是返回的是代理物件麼,那怎麼又強制轉換成Employee了呢,其實代理類是Employee的子類,它具有了比父類更強的能力(資料庫查詢),這個類是怎麼生成的呢?其實hibernate使用了asm.jar和cglig-2.1.3.jar,在記憶體中修改Employee類的位元組碼,修改後的位元組碼只要符合class檔案的規則,就可以創建出代理物件。
Domain物件不應該final的,大家現在應該明白為什麼了吧,如果是final的,那麼就不可以繼承,當然也就不可以產生代理物件,也就不能實現懶載入了,如果你不用懶載入,那麼把domain物件設計成fianl的也是可以的。