1.场景描述:

某项目使用hibernate,在切换到dubbo后,在构造结果对象时从延迟加载对象中获取数据时,报

1
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

构造结果对象的操作没有在事务环境下执行.

2.原因分析:

cxf不报错是因为在web.xml中配置了

1
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
,在请求到达web filter后,创建了
1
EntityManager
,请求结束后关闭
1
EntityManager
.在请求线程处理过程中,都可以拿到
1
EntityManager
,所以不会报错(至少可以从ThreadLocal中拿到).

切换为dubbo后,请求不会经过web filter,在事务模版代码中执行业务操作,可以正确的拿到

1
EntityManager
,不会报错.但是执行到构造结果对象时,就悲剧了.

3.解决办法:

1.修改模版方法,把构造结果对象部分的代码也放到事务中执行.

2.编写支持dubbo的OpenEntityManagerInViewFilter

可以通过

1
TransactionSynchronizationManager
做到如果
1
EntityManagerFactory
在线程变量中不存在则创建
1
EntityManager
,服务处理结束时,关闭
1
EntityManager
.

4.优劣分析

  1. 性能考虑

    1
    open session in veiw
    
    模式还是不怎么优雅,事务执行链路太长了,会影响性能.而且对于我们提供的服务接口来说,构造结果对象已经是最后一步了,后面再也不需要延迟加载对象,不需要在filter里面来做此操作.

    web应用有在渲染模版时读取延迟加载对象的场景,这种场景使用还有意义.

  2. 功能角度

    如果遇到应用内的两个dubbo服务调用,dubbo会走injvm协议.此时请求不会经过io栈,但是会执行所有的dubbo filter.

    比如外部请求调用服务A,服务A调用内部服务B.

    外部请求调A时,filter创建

    1
    EntityManager
    
    ,然后调用服务B时,filter不创建
    1
    EntityManager
    
    ,但是在请求B结束时,filter关闭了
    1
    EntityManager
    
    .在请求A中处理剩下的业务逻辑,如果遇到要操作数据库,就只有哭了.

    为什么web请求就不怕这种filter重入呢?web请求在forward时,你必须把request对象带进去,所以可以在request对象的attribute里面记录是否进过了这个filter.可以参考

    1
    org.springframework.web.filter.OncePerRequestFilter
    
    .但是调用dubbo时,你只需要拿到服务代理对象就ok了,没有办法来知道整个请求链的情况.

5.最后结论

还是修改下我们自己的代码,把构造结果对象部分的代码也放到事务中执行.

一次技术问答

## 一次技术问答最近一年多都没有写博客了,技术上做了很多有意义的事情,也有一些经验上的积累,逐步沉淀到博客上。今天回答某公司的技术上的一些疑问,把问题和回答贴上来。逐步`养`自己的技术观。### 1. 如何做数据安全防范?还有哪些支付安全需要注意?数据安全防范主要分为两个...… Continue reading

2016年05月Reading Notes

Published on August 10, 2016

2016年05月Reading Notes

Published on June 19, 2016