问题背景:
一个新同事接手一个对外接口调试的工作,不知道为什么,需要新开一个线程将对方传过来的数据持久化到本地数据库。就当他开心的开线程保存数据时,bug如约而至。看了问题,大致是因为没有session(本地系统ORM用的是hibernate)。
在网上找了还一会,还真有这方面的解释。
1、关于SessionFactory和Session
SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一 个SessionFactory并从中获取Session实例,而Session不是线程安全的。Session中包含了数 据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱。
2、session的创建:
Hibernate中的Session通过SessionFactory来创建,具体的创建方式有两种:
SessionFactory.getCurrentSession()、SessionFactory.openSession() ;
这两种创建Session的方式主要有如下差别:
a:getCurrentSession创建的session会绑定到当前线程,而openSession不会。
b:getCurrentSession创建的线程会再事务回滚或事务提交后自动关闭,而openSession必须手动关闭
c:getCurrentsession需要在hibernate.cfg.xml文件中添加配置: <propertyname="current_session_context_class">thread</property>
关于current_session_context_class的设置参见:
3、current_session_context_class类型
这个参数设置hibernate管理session的方式(session的范围)。hibernate共有三种方式并且都对应三个实现类实现接口org.hibernate.context.spi.CurrentSessionContext:
a、thread(org.hibernate.context.ThreadLocalSessionContext):当前session通过当前执行的线程来跟踪和界定。
这样配置是本地jdbc事物配置,通过getCurrentSession创建session绑定到当前线程。
这种方式创建的session中并没有获得transaction,我们都要手动调用session.beginTransaction()来打开一个活动的事物。
b、jta(org.hibernate.context.internal.JTASessionContext):当前session根据JTA来跟踪和界定
c、manager(org.hibernate.context.internal.ManagerdSessionContext)
4、Spring实现的current_session_context_class类型
tspring整合hibernate后,由spring的TransactionManager管理hibernate的事务,currentSession是绑定到SpringSessionContext的,而不是thread,此时current_session_context_class的设置应该是spring对CurrentContextSession接口的实现类:SpringSessionContext。
a、@Transactional声明的方法执行时,Spring的TransactionManager会自动Open Session,自动开启事务,并且 将此Session绑定到SpringSessionContext(实际上是TransactionSynchronizationManager的Threadlocal的Map中)
b、SessionFactory.getCurrentSession()方法执行时,调用SpringSessionContext.currentSession()从TransactionSynchronizationManager的上下文中查找当前session
c、找到后返回当前Session,找不到返回HibernateException("No Sessionfound for current thread") ;
5、总结
使用hibernate进行数据库操作时,需要session,并且需要对sesssion进行事务管理。hibernate有三种类型,界定了session的作用域。当使用spring来集成hibernate时,spring会帮我们管理session的事务,因此无需我们手动操作事务(提交、回滚等)。
当然在使用session的时候,注意session的范围,当出现no session异常的情况时,查看配置文件,弄清楚设置session界定的范围,然后定位问题找到解决方案。
同事的那个问题最后解决的方法就是在线程操作数据库持久化的方法那里调用了注解@Transactiional来进行事务管理,为当前线程加入session解决了报错问题。
关于current_session_context_class详情看链接:http://blog.csdn.net/irelandken/article/details/7193123
