杰表技术论坛 's Archiver

admin 发表于 2012-5-9 13:13

新Hibernate SessionFactory().getCurrentSession()猫腻(转帖,不错的帖子)

[url=http://liusu.iteye.com/blog/380397]http://liusu.iteye.com/blog/380397[/url][size=14px]今天要用Hibernate做点实验,下载最新版得下来。解压,建项目,从tutorial copy代码。Tutorial里面提到说最新的Hibernate已经不需要用户自己使用ThreadLocal得方式来管理和持有session,而把这种session管理方式内置了,只要依据依据配置就可以用了[/size]

[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black]hibernate.current_session_context_class = jta/thread/managed [color=#0820]//Use thread[/color]  [/color][/list][/font]


[size=14px]HibernateUtil.java [/size]
[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#7f055][b]package[/b][/color] org.hibernate.tutorial.util;  [/color][*]
[*][color=black][color=#7f055][b]import[/b][/color] org.hibernate.SessionFactory;  [/color][*][color=black][color=#7f055][b]import[/b][/color] org.hibernate.cfg.Configuration;  [/color][*]
[*][color=black][color=#7f055][b]public[/b][/color] [color=#7f055][b]class[/b][/color] HibernateUtil {  [/color][*]
[*][color=black]    [color=#7f055][b]private[/b][/color] [color=#7f055][b]static[/b][/color] [color=#7f055][b]final[/b][/color] SessionFactory sessionFactory;  [/color][*]
[*][color=black]    [color=#7f055][b]static[/b][/color] {  [/color][*][color=black]        [color=#7f055][b]try[/b][/color] {  [/color][*][color=black]            [color=#0820]// Create the SessionFactory from hibernate.cfg.xml[/color]  [/color][*][color=black]            sessionFactory = [color=#7f055][b]new[/b][/color] Configuration().configure().buildSessionFactory();  [/color][*][color=black]        } [color=#7f055][b]catch[/b][/color] (Throwable ex) {  [/color][*][color=black]            [color=#0820]// Make sure you log the exception, as it might be swallowed[/color]  [/color][*][color=black]            System.err.println([color=blue]"Initial SessionFactory creation failed."[/color] + ex);  [/color][*][color=black]            [color=#7f055][b]throw[/b][/color] [color=#7f055][b]new[/b][/color] ExceptionInInitializerError(ex);  [/color][*][color=black]        }  [/color][*][color=black]    }  [/color][*]
[*][color=black]    [color=#7f055][b]public[/b][/color] [color=#7f055][b]static[/b][/color] SessionFactory getSessionFactory() {  [/color][*][color=black]        [color=#7f055][b]return[/b][/color] sessionFactory;  [/color][*][color=black]    }  [/color][*]
[*][color=black]}  [/color][/list][/font]


[size=14px]在使用的时候大概都是如此调用: [/size]

[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#7f055][b]private[/b][/color] Long createAndStoreEvent(String title, Date theDate) {  [/color][*]
[*][color=black]    Session session = HibernateUtil.getSessionFactory().getCurrentSession();  [/color][*][color=black]    session.beginTransaction();  [/color][*]
[*][color=black]    Event theEvent = [color=#7f055][b]new[/b][/color] Event();  [/color][*][color=black]    theEvent.setTitle(title);  [/color][*][color=black]    theEvent.setDate(theDate);  [/color][*]
[*][color=black]    session.save(theEvent);  [/color][*]
[*][color=black]    session.getTransaction().commit();  [/color][*]
[*][color=black]    [color=#7f055][b]return[/b][/color] theEvent.getId();  [/color][*][color=black]}  [/color][/list][/font]

[size=14px]很顺利,跑起来也一切正常。 我有一个查询的需求。就是load一个Object对象需求。代码如下: [/size]
[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#7f055][b]private[/b][/color] Event find(Event event) {  [/color][*][color=black]    Session session = HibernateUtil.getSessionFactory().getCurrentSession();  [/color][*][color=black]    [color=#0820]//session.beginTransaction();[/color]  [/color][*]
[*][color=black]    Event load = (Event) session.load(Event.[color=#7f055][b]class[/b][/color], event.getId());  [/color][*]
[*][color=black]    [color=#0820]//session.getTransaction().commit();[/color]  [/color][*]
[*][color=black]    [color=#7f055][b]return[/b][/color] load;  [/color][*][color=black]}  [/color][/list][/font]

[size=14px]我一想,就是一普通的load和查询操作,应该不用开闭Transaction了。但是却报异常了: [/size]

[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black]org.hibernate.HibernateException: get is not valid without active transaction  [/color][/list][/font]


[size=14px]太抓狂了,就一些查询操作也要开闭Transaction。公司有使用过Hiberbate的小伙子说的记得应该是不用的。想想唯一的使用的区别就是得到Session的代码从 [/size]

[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black]HibernateUtil.getSessionFactory().openSession();  [/color][/list][/font]

[size=14px]变为了 [/size]
[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black]HibernateUtil.getSessionFactory().getCurrentSession();  [/color][/list][/font]


[size=14px]难道是这句HibernateUtil.getSessionFactory().getCurrentSession();有猫腻? [/size]
[size=14px]Checkout源码,跟进去看,果然: [/size]
[size=14px]HibernateUtil.getSessionFactory().getCurrentSession()将Session交给一个CurrentSessionContext来处理,根据配置,使用的是ThreadLocalSessionContext这个东东。查看他的源码: [/size]

[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#7f055][b]public[/b][/color] [color=#7f055][b]final[/b][/color] Session currentSession() [color=#7f055][b]throws[/b][/color] HibernateException {  [/color][*][color=black]        Session current = existingSession( factory );  [/color][*][color=black]        [color=#7f055][b]if[/b][/color] (current == [color=#7f055][b]null[/b][/color]) {  [/color][*][color=black]            current = buildOrObtainSession();  [/color][*][color=black]            [color=#0820]// register a cleanup synch[/color]  [/color][*][color=black]            current.getTransaction().registerSynchronization( buildCleanupSynch() );  [/color][*][color=black]            [color=#0820]// wrap the session in the transaction-protection proxy[/color]  [/color][*][color=black]            [color=#7f055][b]if[/b][/color] ( needsWrapping( current ) ) {  [/color][*][color=black]                current = wrap( current );[color=#0820]//Warp Here????[/color]  [/color][*][color=black]            }  [/color][*][color=black]            [color=#0820]// then bind it[/color]  [/color][*][color=black]            doBind( current, factory );  [/color][*][color=black]        }  [/color][*][color=black]        [color=#7f055][b]return[/b][/color] current;  [/color][*][color=black]    }  [/color][/list][/font]


[size=14px]发现这里的Session已经是不纯洁了,已经成宋祖德嘴里的女明星了。被包了。看看被包的过程, [/size]
[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#7f055][b]protected[/b][/color] Session wrap(Session session) {  [/color][*][color=black]        TransactionProtectionWrapper wrapper = [color=#7f055][b]new[/b][/color] TransactionProtectionWrapper( session );  [/color][*][color=black]        Session wrapped = ( Session ) Proxy.newProxyInstance(  [/color][*][color=black]                Session.[color=#7f055][b]class[/b][/color].getClassLoader(),  [/color][*][color=black]                SESS_PROXY_INTERFACES,  [/color][*][color=black]                wrapper  [/color][*][color=black]            );  [/color][*][color=black]        [color=#0820]// yick!  need this for proper serialization/deserialization handling...[/color]  [/color][*][color=black]        wrapper.setWrapped( wrapped );  [/color][*][color=black]        [color=#7f055][b]return[/b][/color] wrapped;  [/color][*][color=black]    }  [/color][/list][/font]

[size=14px]被包的很过分,直接换成代理了,看看代理人的嘴脸,这个代理人是TransactionProtectionWrapper [/size]

[size=14px]看看他是用什么好东西包的: [/size]
[font=Monaco,][b]Java代码  [url=http://liusu.iteye.com/blog/380397][img]http://liusu.iteye.com/images/icon_star.png[/img][/url][/b]

[list=1][*][color=black][color=#0820]/**[/color] [/color][*][color=black][color=#0820]         * {@inheritDoc}[/color] [/color][*][color=black][color=#0820]         */[/color]  [/color][*][color=black]        [color=#7f055][b]public[/b][/color] Object invoke(Object proxy, Method method, Object[] args) [color=#7f055][b]throws[/b][/color] Throwable {  [/color][*][color=black]            [color=#7f055][b]try[/b][/color] {  [/color][*][color=black]                [color=#0820]// If close() is called, guarantee unbind()[/color]  [/color][*][color=black]                [color=#7f055][b]if[/b][/color] ( [color=blue]"close"[/color].equals( method.getName()) ) {  [/color][*][color=black]                    unbind( realSession.getSessionFactory() );  [/color][*][color=black]                }  [/color][*][color=black]                [color=#7f055][b]else[/b][/color] [color=#7f055][b]if[/b][/color] ( [color=blue]"toString"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"equals"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"hashCode"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"getStatistics"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"isOpen"[/color].equals( method.getName() ) ) {  [/color][*][color=black]                    [color=#0820]// allow these to go through the the real session no matter what[/color]  [/color][*][color=black]                }  [/color][*][color=black]                [color=#7f055][b]else[/b][/color] [color=#7f055][b]if[/b][/color] ( !realSession.isOpen() ) {  [/color][*][color=black]                    [color=#0820]// essentially, if the real session is closed allow any[/color]  [/color][*][color=black]                    [color=#0820]// method call to pass through since the real session[/color]  [/color][*][color=black]                    [color=#0820]// will complain by throwing an appropriate exception;[/color]  [/color][*][color=black]                    [color=#0820]// NOTE that allowing close() above has the same basic effect,[/color]  [/color][*][color=black]                    [color=#0820]//   but we capture that there simply to perform the unbind...[/color]  [/color][*][color=black]                }  [/color][*][color=black]                [color=#7f055][b]else[/b][/color] [color=#7f055][b]if[/b][/color] ( !realSession.getTransaction().isActive() ) {  [/color][*][color=black]                    [color=#0820]// limit the methods available if no transaction is active[/color]  [/color][*][color=black]                    [color=#7f055][b]if[/b][/color] ( [color=blue]"beginTransaction"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"getTransaction"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"isTransactionInProgress"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"setFlushMode"[/color].equals( method.getName() )  [/color][*][color=black]                         || [color=blue]"getSessionFactory"[/color].equals( method.getName() ) ) {  [/color][*][color=black]                        log.trace( [color=blue]"allowing method ["[/color] + method.getName() + [color=blue]"] in non-transacted context"[/color] );  [/color][*][color=black]                    }  [/color][*][color=black]                    [color=#7f055][b]else[/b][/color] [color=#7f055][b]if[/b][/color] ( [color=blue]"reconnect"[/color].equals( method.getName() )  [/color][*][color=black]                              || [color=blue]"disconnect"[/color].equals( method.getName() ) ) {  [/color][*][color=black]                        [color=#0820]// allow these (deprecated) methods to pass through[/color]  [/color][*][color=black]                    }  [/color][*][color=black]                    [color=#7f055][b]else[/b][/color] {  [/color][*][color=black]                        [color=#7f055][b]throw[/b][/color] [color=#7f055][b]new[/b][/color] HibernateException( method.getName() + [color=blue]" is not valid without active transaction"[/color] );  [/color][*][color=black]                    }  [/color][*][color=black]                }  [/color][*][color=black]                log.trace( [color=blue]"allowing proxied method ["[/color] + method.getName() + [color=blue]"] to proceed to real session"[/color] );  [/color][*][color=black]                [color=#7f055][b]return[/b][/color] method.invoke( realSession, args );  [/color][*][color=black]            }  [/color][*][color=black]            [color=#7f055][b]catch[/b][/color] ( InvocationTargetException e ) {  [/color][*][color=black]                [color=#7f055][b]if[/b][/color] ( e.getTargetException() [color=#7f055][b]instanceof[/b][/color] RuntimeException ) {  [/color][*][color=black]                    [color=#7f055][b]throw[/b][/color] ( RuntimeException ) e.getTargetException();  [/color][*][color=black]                }  [/color][*][color=black]                [color=#7f055][b]else[/b][/color] {  [/color][*][color=black]                    [color=#7f055][b]throw[/b][/color] e;  [/color][*][color=black]                }  [/color][*][color=black]            }  [/color][*][color=black]        }  [/color][/list][/font]


[size=14px]呵呵,几乎所有正常的操作都必须在transcation.isActive()条件下才能执行。我要用的get,load,save, saveOrUpdate,list都在此列。 [/size]

[size=14px]到此为止,算明白了。 寻到根了,Hibernate的理由是: [/size]

[size=14px]org.hibernate.context.ThreadLocalSessionContext [/size]

[size=14px]A CurrentSessionContext impl which scopes the notion of current session by the current thread of execution. Unlike the JTA counterpart, threads do not give us a nice hook to perform any type of cleanup making it questionable for this impl to actually generate Session instances. In the interest of usability, it was decided to have this default impl actually generate a session upon first request and then clean it up after the org.hibernate.Transaction associated with that session is committed/rolled-back. In order for ensuring that happens, the sessions generated here are unusable until after Session.beginTransaction() has been called. If close() is called on a session managed by this class, it will be automatically unbound. [/size]

[size=14px]Additionally, the static bind and unbind methods are provided to allow application code to explicitly control opening and closing of these sessions. This, with some from of interception, is the preferred approach. It also allows easy framework integration and one possible approach for implementing long-sessions. [/size]

[size=14px]The buildOrObtainSession, isAutoCloseEnabled, isAutoFlushEnabled, getConnectionReleaseMode, and buildCleanupSynch methods are all provided to allow easy subclassing (for long-running session scenarios, for example). [/size]

[size=14px]Author: [/size]

[size=14px]Steve Ebersole [/size]

[size=14px]用别人的东西得小心,知道他背后干了什么很重要啊。[/size]

页: [1]

Powered by Discuz! Archiver 6.1.0  © 2001-2007 Comsenz Inc.