1.前言
今天在整合SSH框架的时候,当在表现层调用bean层查找数据的时候,报错,具体如下所示
根据字面上的意思,应该是代理不能被初始化,session已经关闭,这篇博客就来解决一下这个问题。
2.NoSession问题
1.原因分析
当执行Session的load()方法时,Hibernate不会立即执行查询CUSTOMERS表的select语句,仅仅返回Customer类的代理类的实例,这个代理类具由以下特征:
(1)由Hibernate在运行时动态生成,它扩展了Customer类,因此它继承了Customer类的所有属性和方法,但它的实现对于应用程序是透明的。
(2)当Hibernate创建Customer代理类实例时,仅仅初始化了它的OID属性,其他属性都为null,因此这个代理类实例占用的内存很少。
(3)当应用程序第一次访问Customer代理类实例时(例如调用customer.getXXX()或customer.setXXX()方法),Hibernate会初始化代理类实例,在初始化过程中执行select语句,真正从数据库中加载Customer对象的所有数据。但有个例外,那就是当应用程序访问Customer代理类实例的getId()方法时,Hibernate不会初始化代理类实例,因为在创建代理类实例时OID就存在了,不必到数据库中去查询。
提示:Hibernate采用CGLIB工具来生成持久化类的代理类。CGLIB是一个功能强大的Java字节码生成工具,它能够在程序运行时动态生成扩展 Java类或者实现Java接口的代理类。关于CGLIB的更多知识,请参考:
Spring学习笔记五(JDKProxy和cglibProxy的区别)
。
因此从字面上我们了解到是在表现层调用的时候,session已经关闭。因为Hibernate中如果采用load加载的话,默认的是延迟加载,也就是lazy=true操作,因此,当调用完load后,session即可关闭,如果此时再次打印输出值得话,就会报上面的错误,因为我们的session只是放置到了Dao层。表现层根本获取不到。
public void load() { //调用延迟加载 User user1 = userService.getById(1); //下面这句话就报错 System.out.println(user1.getName()); System.out.println("----------------------"); User user2 = userService.getById(1); System.out.println(user2.getName()); System.out.println("----------------------"); }2.方法一
既然已经分析出来是延迟加载的原因,因此我们可以关闭延迟加载的属性,也就是不延迟加载lazy=false,但是只有确定了该业务不需要延迟加载的时候,才考虑这种设计。这是一种解决的办法,但是有点死板,把这么好的延迟加载的特性给活生生的去除了。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.tgb.itoo.base.entity"> <!-- 去除延迟加载的属性 --> <class name="User" table="test_user" lazy="false" > <id name="id"> <generator class="native"/> </id> <property column="name" generated="never" lazy="false" length="255" name="name" type="string"/> </class> </hibernate-mapping>3.方法二
我们可以把session的生命周期放置到表现层,这样的话,就可以在应用延迟加载特性下,也可以获取想要的数据。这时通常采取的操作时OpenSessionView,在表现层的web.xml中进行配置即可
<filter> <filter-name>openSessionInView</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>4.方法三
第一种办法,采取的是关闭全局的延迟加载,有点太死板。在hibernate中我们可以根据我们的业务需要动态的关闭延迟加载的属性。采取的操作就是Hibernate.initialize(user);但在使用时需要注意的一点是:其中的proxy是持久对象的关联对象属性,比如A实体,你要把A的关联实体B也检出,则要写Hibernate.initialize(a.b)。
public User getById(int id) { User user = userEao.getById(id); Hibernate.initialize(user); return user; }
版权声明:本文为博主原创文章,未经博主允许不得转载。