`
liusu
  • 浏览: 171343 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

新Hibernate SessionFactory().getCurrentSession()猫腻

    博客分类:
  • Java
阅读更多
今天要用Hibernate做点实验,下载最新版得下来。解压,建项目,从tutorial copy代码。Tutorial里面提到说最新的Hibernate已经不需要用户自己使用ThreadLocal得方式来管理和持有session,而把这种session管理方式内置了,只要依据依据配置就可以用了

hibernate.current_session_context_class = jta/thread/managed //Use thread


HibernateUtil.java
package org.hibernate.tutorial.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}


在使用的时候大概都是如此调用:

    private Long createAndStoreEvent(String title, Date theDate) {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();

        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);

        session.save(theEvent);

        session.getTransaction().commit();

        return theEvent.getId();
    }

很顺利,跑起来也一切正常。 我有一个查询的需求。就是load一个Object对象需求。代码如下:
	private Event find(Event event) {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		//session.beginTransaction();

		Event load = (Event) session.load(Event.class, event.getId());

		//session.getTransaction().commit();

		return load;
	}

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

org.hibernate.HibernateException: get is not valid without active transaction


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

HibernateUtil.getSessionFactory().openSession();

变为了
HibernateUtil.getSessionFactory().getCurrentSession();


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

public final Session currentSession() throws HibernateException {
		Session current = existingSession( factory );
		if (current == null) {
			current = buildOrObtainSession();
			// register a cleanup synch
			current.getTransaction().registerSynchronization( buildCleanupSynch() );
			// wrap the session in the transaction-protection proxy
			if ( needsWrapping( current ) ) {
				current = wrap( current );//Warp Here????
			}
			// then bind it
			doBind( current, factory );
		}
		return current;
	}


发现这里的Session已经是不纯洁了,已经成宋祖德嘴里的女明星了。被包了。看看被包的过程,
protected Session wrap(Session session) {
		TransactionProtectionWrapper wrapper = new TransactionProtectionWrapper( session );
		Session wrapped = ( Session ) Proxy.newProxyInstance(
				Session.class.getClassLoader(),
		        SESS_PROXY_INTERFACES,
		        wrapper
			);
		// yick!  need this for proper serialization/deserialization handling...
		wrapper.setWrapped( wrapped );
		return wrapped;
	}

被包的很过分,直接换成代理了,看看代理人的嘴脸,这个代理人是TransactionProtectionWrapper

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


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

到此为止,算明白了。 寻到根了,Hibernate的理由是:

org.hibernate.context.ThreadLocalSessionContext

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.

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.

The buildOrObtainSession, isAutoCloseEnabled, isAutoFlushEnabled, getConnectionReleaseMode, and buildCleanupSynch methods are all provided to allow easy subclassing (for long-running session scenarios, for example).

Author:

Steve Ebersole

用别人的东西得小心,知道他背后干了什么很重要啊。
分享到:
评论
11 楼 wm309 2014-08-21  
12233
10 楼 liusu 2012-10-20  
muzi131313 写道
很是佩服楼主解决问题的思路,debug,程序员生存的必备技能。。。


只有有合适的工具,DEBUG也是一种乐趣啊。。。 看内存流转。
9 楼 muzi131313 2012-09-28  
很是佩服楼主解决问题的思路,debug,程序员生存的必备技能。。。
8 楼 threepi 2012-08-15  
不错,写的很好,我也遇到类似问题,到处找源码!
getCurrentSession 必须要和 Transaction 相关联,Spring 3.x Hibernate 4.x 整合中报 org.hibernate.HibernateException: No Session found for current thread
7 楼 niweiwei 2012-06-03  
哥们,你是怎么解决这个问题的?
我用的是Spring框架
6 楼 howareyouo 2012-05-06  
beginTransaction一下
5 楼 howareyouo 2012-05-06  
恩, 任凭我怎么找我也打不到解决的办法, 到您这一样我就明白了, 大不了我每次都
4 楼 liusu 2011-12-17  
这个解决的办法有很多啊。。。 如果你是使用Spring的话,默认打开Transaction就可以了。 如果你不是使用Spring,或者没有DI框架,就是用JDK自带的Dynamic Proxy 给所有调用这个Session的方法都打开Transaction就可以了啊。。。。
3 楼 gaolinwu 2011-12-14  
说了半天,没看见你最终是怎么解决的 :
2 楼 liusu 2009-05-07  
发现Hibernate的使用不容易啊 呵呵
1 楼 43385607 2009-05-06  
沙发,学习了

相关推荐

    SessionFactory.getCurrentSession与openSession的区别

    理解 `SessionFactory.getCurrentSession()` 和 `Session.openSession()` 的区别对于优化和正确使用Hibernate至关重要。 首先,`SessionFactory` 是Hibernate中的一个接口,它是一个全局的、线程安全的工厂类,用于...

    HibernateSessionFactory.java

    MyEclipse生成的HibernateSessionFactory.java文件,应该是完整的。

    HibernateSessionFactory.java Hibernate使用的整合的工具文件

    Session s= HibernateSessionFactory.getSession(); 就是Hibernate的工具java类

    hibernate3.zip 包含hibernate3.jar

    `SessionFactory`是从Hibernate配置文件中读取信息并构建的,用于优化性能和减少数据库连接的创建。 4. **Criteria查询**:除了SQL和HQL(Hibernate查询语言),Hibernate还提供了`Criteria` API进行动态查询。...

    使用 HibernateSessionFactory 类

    在`HibernateSessionFactory.java`这个文件中,我们可能看到对上述过程的封装,例如创建`SessionFactory`的静态方法,以及提供会话的获取和关闭功能。这样的封装有助于代码的整洁和复用。 在实际应用中,`...

    org.springframework.orm.hibernate3.LocalSessionFactoryBean

    sessionFactory.setHibernateProperties(hibernateProperties); return sessionFactory; } @Bean public DataSource dataSource() { // ... 数据源配置 } } ``` #### 五、检查与排除其他可能的问题 1. **...

    HibernateSessionFactory 代码

    Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); MyEntity entity = new MyEntity(); // 设置属性... session.save(entity); tx.commit(); session.close(); ``` *...

    Spring整合hibernate

    随着Hibernate的更新,特别是SessionFactory.getCurrentSession()的出现,可以直接在Spring的事务范围内获取和管理Session,使得直接使用HibernateAPI成为可能。 2. **使用SessionFactory.getCurrentSession()** ...

    springboot集成hibernate

    Session session = sessionFactory.getCurrentSession(); return (User) session.get(User.class, id); } @Override public void addUser(User user) { Session session = sessionFactory.getCurrentSession...

    Hibernate笔记总结

    Session session = HibernateSessionFactory.getSession(); Transaction tx = session.beginTransaction(); session.save(user); // 保存用户 tx.commit(); HibernateSessionFactory.closeSession(); } ``` ...

    hibernate数据库通用SQL代码

    Session session = HibernateSessionFactory.currentSession(); Transaction t = session.beginTransaction(); session.save(o); t.commit(); HibernateSessionFactory.clossSession(); } ``` 2. 删除数据...

    Struts2.X+Hibernate3.X+Spring2.5 整合

    接下来,Spring和Hibernate的整合主要体现在Spring管理Hibernate的SessionFactory,以及事务管理。通过Spring的HibernateTemplate或SessionFactoryBean,可以在不直接接触Session的情况下进行数据库操作,同时利用...

    ssh整合,不带hibernate.cfg.xml的方式

    在Spring中,`LocalSessionFactoryBean`是用于创建Hibernate SessionFactory的一个bean。SessionFactory是Hibernate的核心对象,负责创建Session实例,而Session是与数据库交互的主要接口。为了不依赖`hibernate.cfg...

    spring-hibernate3.jar.zip

    其次,Spring的ApplicationContext可以加载Hibernate的相关配置,如SessionFactory,进而创建Session对象,进行数据库操作。同时,Spring还可以通过Bean工厂来管理Hibernate的实体类,实现数据持久化。 在配置上,...

Global site tag (gtag.js) - Google Analytics