ssh中Hibernate懒加载,session问题的学习与理解
交代本项目中要求获取session的方式如下:
public Session getCurrentSession() { // 增删改使用的session,事务必须是开启的(Required,即propagation="REQUIRED"),否则获取不到 return sessionFactory.getCurrentSession(); } public Session getQueryCurrentSession() { // 查询使用的session,,该生成的session没有事务。 return sessionFactory.openSession(); }
问题1描述:
Component对Arch为多对多的关系,小明在页面需要展示Component的记录,然后展示出来有一条记录a是重复的,经过一番排查,发现记录a在中间表中对应着两条
记录b,c。即因为Component与Arch是多对多的关系,故在中间表中有两条a对应着不同的b,c。那为什么仅仅只是查询Component,页面会显示两条重复的a记录呢?原
来,本次项目中关闭了懒加载,开启了立即加载,并且小明在查询Component的时候使用的QBC查询。
1.什么是懒加载(延迟加载)?什么是立即加载?
当数据在需要用到的时候才去获取就是懒加载;不管数据用不用到一次性全部查询获取出来。由此可见立即加载会造成资源浪费,并且导致查询效率低下。
2.什么是QBC查询
QBC,即Query By Criteria,是Hibernate提供的一种查询方式,不需要写hql语句,直接使用方法实现,省略QBC的使用方法与步骤,通过如下配置让其在控制台打印
出对应的sql语句,来进行观察。
<prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop>
可以从控制台打印的一些sql语句,看出只要在实体类中配置了关联关系在进行QBC查询的时候,都会进行left outer join左外连接查询,a a aa a a ,好奇害死猫呀,这
就又让我去看看普通的hql语句是不是这样做的呢?果然不同。。。真的是,QBC与HQL两种查询的区别,大致在于QBC查询,只要在实体类中配置了关联关系在进行查
询的时候,都会进行left outer join左外连接查询,而hql查询是根据表字段来进行查询的并没有进行表关联查询,所以有可能就算你使用了立即加载,在查询的时候也不会
出现有重复记录的bug,a a a a a,也就不会有现在这一大堆的思考了。。不过这样也是很好很好的,让我又明白了并学习了当初并不懂的一些知识。。啊哈哈哈。。。。
回到上面的问题,重复记录的原因:因为使用了立即加载模式,查询的时候,多对多关联关系的Arch表进行了中间表的关联 进行了左外连接查询 故出现了2条重复的记录a
解决记录重复问题:
1.采用立即加载,在查询的时候,添加去重distinct
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
2.关闭立即加载,使用懒加载(延迟加载),这样在查询Component的时候就不会进行中间表的左外连接查询,也就不会有重复记录
问题2描述:
使用方法1,跟使用方法2都可以解决问题,但是,使用方法2,现在又出了问题。使用方法2,可以解决小明重复的问题,但是凤姐又出问题了,由于凤姐通过
Component的id查询到的Component,是在dao层使用的openSession()获取的Session,即在dao层中就已经将session关闭了,现在又使用了懒加载,故凤姐在action表
现层无法通过Component对象来get到对应的Arch集合,这个时候会抛no session的异常。说完,要是跟我一样是小白新手的话,可能是懵比的,那就再简单介绍下本
此项目中所涉及到的知识:(也是折腾了一两天,才对其中的知识懵懵懂懂!!!!!!!!,只是个人表面肤浅的理解。。。。)
3.获取session的两种方式?
1.openSession(),直接打开一个新的session。需要自己手动去关闭。
2.getCurrentSession(),获取当前session,当session不存在,就新打开一个,否则获取已经有的session。不需要自己手动关闭,交由spring管理,当service层的事务被
提交完成后,session会自动关闭。
4.为什么要配置增删改获取的session,要在service层自动关闭?
了解以下hibernate事务提交过程就会知道了:
..... Session session=sf.openSession(); Transaction tx=session.beginTransaction(); ..... tx.commit(); s.close();
1.创建session实例
2.用session创建Transaction实例,开始一个事务。
3.利用session方法进行持久化操作。将实体对象的持久化到数据库中。
4.提交操作结果,结束事务。对实体对象的持久化操作结束后,必须提交事务。
5.关闭session,与数据库断开连接
知道了事务提交的过程,也就明白了为什么本次项目会要求增删改操作使用getCurrentSession(),查询操作使用openSession()。因为增删改要有事务的操作,而事务是作
用在service业务层的,并且是事务提交之后,session才能关闭。故不能在dao层立即关闭session,故不用openSession()。因为查询不需要事务,故使用openSession(),
也就不需要在查询的时候开启事务了,以免造成资源的浪费,就可以直接在dao层关闭session,对事务不造成影响。但是但是但是,问题来了,这就要求我们只能使用立
即加载,而不能使用延迟加载了,因为涉及到查询的操作,都是在dao层关闭了了session,在service层或表现层根本不能根据对象去获取多对多关系的集合属性,因为
session已经在dao层就关闭了。
所以绕来绕去,解决本次项目问题的方法,还是只能采用立即加载.
对于只能使用立即加载,当数据多了以后,这也会造成效率低的情况,所以最好建议不要这么做,但是这也是没有办法的事,毕竟现在本项目要求查询用的是
openSession(),并立即关闭。但是如果抛开本次项目,该如何解决懒加载的问题呢?如何做到使用懒加载而不抛异常呢?
个人认为只能更改本次项目中,查询操作 获取session的方式,将openSession()也换为getCurrentSession(),虽然现在查询也需要加上事务了(事务必须是开启的,即
propagation="REQUIRED",否则通过getCurrentSession()是获取不到session的。),但是我也不知道,这个立即加载造成的效率跟查询开启事务,来比较,谁比较浪费资
源????????下面就说下,当查询操作的session变为getCurrentSession()后,如何解决懒加载问题。
再次声明此方法前提:在dao层以getCurrentSession()获取session来查询对象,这个时候dao层不会立即关闭sesion,交由spring来管理,在service层,当事务提交完成后,session会自动关闭。
项目中涉及到懒加载的位置,一般如下
1..在业务层service中,需要使用懒加载
这个时候session还没有关闭,就随便用啦
2.在表现层action中,需要使用懒加载
由于现在session,已经被关闭,所以在表现层中再用对象去获取多对多关系的集合属性,是会抛懒加载no session异常的。那么,如何解决?
解决懒加载(延迟加载获取不到对象的关联属性值)问题:
1.在session关闭前,即在业务层中 将要获取的数据强行进行初始化,即初始化需要用到的集合属性,这样在表现层action中就再次获取的时候就是可以的,这里的
强行有很多种方法的,我就说下我用到的方法吧:
2.延长session的生命周期,让其在一次响应后结束(quest,response)后,才被关闭。
具体做法:在web.xml中配置
<!-- 延长session的生命周期: 关联关系 需要加载的时候就去查找 不需要用到的时候就不查找 比如:加载部门名字 这个时候em.dm.name会用到关联关系 --> <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>
===============================================================
真的是整理的好累好累,虽然还是不咋地,但是就这样吧。啊啊啊啊啊。。。。。。。。。。。。。。。。
思考花絮:
1.凤姐现在在Component模块中需要用到对应的Arch对象
实现方法:通过Component的id获取到Component后,通过Component对象来get到对应的Arch集合
由于在service层通过id获取到Component采用OpenSesssion()打开session 要在service层手动关闭session
故当在action表现层就获取不到对应的Arch集合,因为此时是延迟加载模式,然后session又已经被关闭了
如何解决?
方法1:采用立即加载模式
方法2:采用延迟加载模式,在service层通过getCurrentSession()方式获取session,此中方式会在事务完成后
(本查询结束离开service层)自动进行提交,然后在web.xml里面配置spring的openSessionInView,来延长session的
生命周期,让其不自动在离开service层就关闭,而是让session在一次请求响应(request,response)结束后才进行自动关闭,
即加上这个配置后,在session在action表现层也是可以作用的。此时就可以在变现曾拿到对应的Arch对象
1.session
如何获取session?两种方式
两种方式的区别
2.懒加载
何为懒加载?懒加载有什么作用?
如何解决项目中懒加载问题?
立即加载与懒加载如何选择?
使用立即加载带来的问题?
比如使用hql查询与qbc查询的区别 而带来的异常
hql查询不使用关联关系查询 直接利用表字段什么的
qbc查询使用hibernate.format_sql在控制台打印数据库查询语句 可以观察到 只要配置了关联关系
就会发出左外连接查询left join
因此 使用qbc查询 当存在多对多的关系时,开启了立即加载的模式,此时,查询 会出现一条记录重复的现象
解决办法:1.去重 distinct
2.使用延迟加载
那么问题来了 对于在action表现层 如何使用延迟加载 需要使用spring来延长session的生命周期(request,response结束后才关闭session)或者使用getCurrentSession()来获取session,并在session关闭前,将要使用的数据进行初始化获取
对于小白,对,没错,说的就是我这样的, 啊哈哈哈哈。。。一些业务方法 会喜欢写在表现层 最好的是 把一些处理业务逻辑的方法 写在业务层 这样 也就不用延长session的生命周期了 真的是