hibernate笔记(四)关于懒加载和load()方法之一

本文深入探讨Hibernate中的懒加载机制,包括其工作原理、代理对象的生成方式及其与正常对象的区别。并通过实例演示如何避免懒加载异常,以及在不同场景下如何正确使用懒加载。

重点牢记

 

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="hiber.domain">  
  
 <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"/>  
        </id>  
        <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.<a href="http://lib.youkuaiyun.com/base/javase" class='replace_word' title="Java SE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a>: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.EmployeeEnhancerByCGLIB

5a7cc325,名字很奇怪吧,这个对象是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的也是可以的。

关于异常

Junit could not initialize proxy - no Session (Hibernate Open session view)

hibernate 中的lazy加载网上很多,一般都会用opensession view解决。

但是open seesion view是针对web访问的,在Junit测试类中无法使用就无法解决了。

最后的解决办法是junit 继承 AbstractTransactionalJUnit4SpringContextTests,交给transaction管理,就可以了。

转载于:https://my.oschina.net/java1314/blog/855562

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值