读书人

延迟加载时为啥不自动open session呢

发布时间: 2012-11-21 08:23:26 作者: rapoo

延迟加载时为什么不自动open session呢?
用了hibernate都有差不多2年时间,一直都没有深入研究,最近学习起它的延迟加载。
在网上找了很多资料,它们都是说,使用延迟加载时,被加载对象(也就是那个proxy)必须是同一个session打开的前提下,才能获取数据,否则,会抛出session is closed 或 disconnection session 异常。
对此,我有个疑问,为什么hibernate在检测到与proxy相关联的session 是close或disconnection 时,不自动open,反而是抛异常。可能是为了事务的一致性考虑。
了解这个问题的目的是要找出一种方法替代opensessioninview,我认为,延迟加载可以用于2个场景中,一个是从数据库取出数据,并传递到view层展现;另一个是在后台处理的时候。若是第一种场景的话,我觉得proxy取数据时,自己打开session并以只读方法加载数据,并不需要用opensessioninview的长连接。至于后一种在后台处理时,大可以把所需要的数据预先取出,而不需要使用延迟加载。
希望各位发表一下意见。 1 楼 robbin 2008-03-13 事情没你想像的那么简单。Hibernate通过Session的一级缓存来管理持久化对象的状态,当Session关闭之后,持久化对象究竟处于何种状态是未知的,此时如果自动打开第2个Session,这个Session的缓存里面没有这个持久化对象的引用关系,因此无法判断对象状态,那么就很有可能带来一些不可预知的后果,比方说应该update的时候却没有update,应该级联插入却没有插入等等。

2 楼 hrtc 2008-03-13 好像可以配一个拦截器,当关闭时自动打开(spring中) 3 楼 luckaway 2008-03-13 我也有个疑问。sessin关闭之后应该只与数据库断开连接,数据还保存在session中,且此session与用户关联!
ps:
hibernate的源码太复杂了,根本看不懂 4 楼 huangjiej 2008-03-14 robbin说的的确有道理,2个session处理同一个对象时,很容易会出现不可预料的结果。但是,请想一下延迟加载的使用场景,一般是在后台查询数据然后在前台部分显示,在这种情况下,打开的第二个session只是用于数据的加载,并不会对相关数据进行增删改操作。与此同时,我认为session的生存期是比较短的,如在opensessioninview的模式中,session会在请求结束时close掉,并且会被回收,所以当我打开第二个session加载数据,完毕后关闭,不使用第二个session执行增删改操作,理论上是不会出现这种不可预知的后果。
因此,我观点是在view层展示数据的时候,session reconnect或者新开一个session去取lazy数据,这种方法是可取的。不知robbin有何看法。
5 楼 hrtc 2008-03-14 hibernate里应该不能2个session里处理同一个对象的,这里的对象是指session维护的java对象,一个对象只能被一个session处理,但多个session可以处理数据库中同一条记录,session close后只是清除缓存(快照并非里面的对象),只要有引用指着原有对象,该对象不会释放,再重连后我觉得还是同一个session,lz你用了延迟加载,取数据时当然会去查数据库。另外你可以试试,先查一个对象放入httpsession中,再在另一个请求中save这个对象,就会报错,具体报什么我忘了。以上是我的理解,不知正不正确。 6 楼 robbin 2008-03-14 huangjiej 写道robbin说的的确有道理,2个session处理同一个对象时,很容易会出现不可预料的结果。但是,请想一下延迟加载的使用场景,一般是在后台查询数据然后在前台部分显示,在这种情况下,打开的第二个session只是用于数据的加载,并不会对相关数据进行增删改操作。与此同时,我认为session的生存期是比较短的,如在opensessioninview的模式中,session会在请求结束时close掉,并且会被回收,所以当我打开第二个session加载数据,完毕后关闭,不使用第二个session执行增删改操作,理论上是不会出现这种不可预知的后果。
因此,我观点是在view层展示数据的时候,session reconnect或者新开一个session去取lazy数据,这种方法是可取的。不知robbin有何看法。


session reconnect是完全可以的,但问题是你还是要持有session,否则你从哪来reconnect?既然你要一直持有session,那就还是OpenSessionInView模式,没啥本质区别,唯一的区别只在于底层的数据库连接是否被分配而已。

如果关闭session再次打开,即使是只读,也一定会报错。这是因为lazy的对象引用关系是关联到当前的session上的,你关闭掉session以后,打开一个新的session,这个新的session和你内存当中的lazy对象没有任何引用关系,所以当你存取lazy对象的时候,lazy对象找不到引用的session,就会报错。


7 楼 huangjiej 2008-03-14 哦,那proxy其实是有绑定session的
我稍稍看一下源码,hibrenate的延迟加载代理对象分成
PersistentCollection 集合类的延迟加载,在其抽象实现类(AbstractPersistentCollection)里,保存了SessionImplementor(保存session与其它hibernate对象的关联,实际上session也继承此接口),只不过是私有变量。
HibernateProxy(好像是代理实体类的),有方法 getHibernateLazyInitializer()取出LazyInitializer,在LazyInitializer身上,我们也能看到SessionImplementor,所以要取出session还是有可能的,只不过,这就带有一定的bad smell,呵呵。
恰巧,我也找到一份这样的代码,其思想是在hibernate抛出session close异常时,会创建一个session并查询数据,(http://www.360works.com/articles/detail/56/),“Hibernate Lazy Loading patch” ,它是基于hibernate2的,直接是修改了hibernate里面的代码.
8 楼 huangjiej 2008-03-14 to hrtc:

"再重连后我觉得还是同一个session",是的,重连是同一个session的,可看我上面回复的内容。
至于“另外你可以试试,先查一个对象放入httpsession中,再在另一个请求中save这个对象,就会报错,具体报什么我忘了” ,这个要分2个情况考虑哦,一种是没有使用lazy的或者lazy的属性的值都已经取出来的,这个应该是没有问题的。另一种是使用了lazy,而且这个对象中的lazy属性没有加载数据,这个很大可能会出问题,因为这时是2个session同时操作一个对象。
我没有试过,抽空测试一把把结果告诉大家。
9 楼 HRoger 2008-04-01 最近,我也碰到了这个问题,感觉延迟加载就像鸡肋,我的客户端是基于Eclipse的rpc的,通过webservice来调用的,很难实现opensessioninview,我只能lazy=“false”来解决了,不知大家对于webservice这样东西,用Hibernate有什么好的建议 10 楼 yujianqiu 2008-04-19 我觉得如果你有这个困惑的话,就有必要去学习一下什么是Session了。
关闭Session不意味着关闭Connection;
长连接是一个相对的概念,opensessioninview不是长连接的代名词。

HRoger的问题,是一个设计问题,也是一个技巧问题。
google一下DTO,也可以查查Hibernate API中Hibernate.initialize()这个方法。 11 楼 xieke 2008-12-12 robbin 写道事情没你想像的那么简单。Hibernate通过Session的一级缓存来管理持久化对象的状态,当Session关闭之后,持久化对象究竟处于何种状态是未知的,此时如果自动打开第2个Session,这个Session的缓存里面没有这个持久化对象的引用关系,因此无法判断对象状态,那么就很有可能带来一些不可预知的后果,比方说应该update的时候却没有update,应该级联插入却没有插入等等。



其实没有想象中这么复杂的,一级缓存存在的主要目的是维护事务独立性,
也就是在事务中的未提交数据在一级缓存中,不会影响到二级缓存,
当事务提交后,一级缓存同步到二级缓存中,
到达页面中的时候,事务当然已经结束,此时一级缓存已经同步到二级缓存中。
归根结底,页面层次只是view级别,
所以新开一个session用于页面延迟加载,也就是新开一个数据库连接用来查看而已,
直接使用二级缓存是没有问题的。

12 楼 elam 2008-12-12 我弱弱的问下拦截器不行么?
拦截器难道算是长连接么? 13 楼 taupo 2008-12-12 opensessioninview并不等于长连接
opensessioninview只是保持session不关闭,但是在一般情况下,当事务提交过后,session会释放数据库连接,但是session并不关闭
当再次需要读取数据库时,session会重新获取连接
14 楼 hypercube1024 2008-12-13 HRoger 写道最近,我也碰到了这个问题,感觉延迟加载就像鸡肋,我的客户端是基于Eclipse的rpc的,通过webservice来调用的,很难实现opensessioninview,我只能lazy=“false”来解决了,不知大家对于webservice这样东西,用Hibernate有什么好的建议

用webservice的话我一般就做一个dto然后用BeanUtils拷贝一下属性了,虽然麻烦一点,不过也是没有办法,总比lazy=false好点,毕竟如果是是集合的话,还是影响性能的,需要什么属性就做一个相应的dto,通过BeanUtils拷贝还是很方便的. 15 楼 crazyfighter 2008-12-14 huangjiej 写道 用了hibernate都有差不多2年时间,一直都没有深入研究,最近学习起它的延迟加载。
在网上找了很多资料,它们都是说,使用延迟加载时,被加载对象(也就是那个proxy)必须是同一个session打开的前提下,才能获取数据,否则,会抛出session is closed 或 disconnection session 异常。
对此,我有个疑问,为什么hibernate在检测到与proxy相关联的session 是close或disconnection 时,不自动open,反而是抛异常。可能是为了事务的一致性考虑。
了解这个问题的目的是要找出一种方法替代opensessioninview,我认为,延迟加载可以用于2个场景中,一个是从数据库取出数据,并传递到view层展现;另一个是在后台处理的时候。若是第一种场景的话,我觉得proxy取数据时,自己打开session并以只读方法加载数据,并不需要用opensessioninview的长连接。至于后一种在后台处理时,大可以把所需要的数据预先取出,而不需要使用延迟加载。
希望各位发表一下意见。

读书人网 >软件架构设计

热点推荐