ssh中Hibernate懒加载,session问题的学习与理解

时间:2022-02-02 17:29:08

交代本项目中要求获取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,也就不会有现在这一大堆的思考了。。不过这样也是很好很好的,让我又明白了并学习了当初并不懂的一些知识。。啊哈哈哈。。。。

ssh中Hibernate懒加载,session问题的学习与理解

  回到上面的问题,重复记录的原因:因为使用了立即加载模式,查询的时候,多对多关联关系的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中就再次获取的时候就是可以的,这里的

强行有很多种方法的,我就说下我用到的方法吧:

ssh中Hibernate懒加载,session问题的学习与理解

  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的生命周期了  真的是