current_session_context_class

本文探讨了在使用Hibernate时遇到的session管理与事务配置问题,特别是如何正确设置hibernate.current_session_context_class属性以适应不同场景的需求。包括在web容器中和单独的Java应用程序中运行时的配置差异,以及在Spring框架下如何调整hibernate配置以确保session与事务的一致性和高效性。重点解释了getCurrentSession()方法的使用方式,以及它与openSession()的区别,同时提供了适用于Java EE环境的session获取方法。此外,文章还深入介绍了ThreadLocal模式在管理Session方面的应用,以及如何通过配置来决定当前session的范围和上下文。最后,文章总结了getCurrentSession()方法的优势,并提供了在hibernate.cfg.xml文件中进行关键配置的步骤。

遇到过的问题:
情景1:
在使用SessionFactory的getCurrentSession方法时遇到如下错误,经过检查,原因如下:
是因为在hibernate.cfg.xml文件中忘记进行了如下设置:
hibernate.current_session_context_class如果是在web容器中运行hibernate,则在hibernate.cfg.xml中加入这句话:
jta
如果是在一个单独的需要进行JDBC连接的java application中运行hibernate,则这样设置:
thread

情景2:
在ssh2中的sessionFactory配置文件中
应将hibernate.current_session_context_class设为org.springframework.orm.hibernate3.SpringSessionContext(默认为此值)
并应用spring管理事务。如果为thread 则会报异常,

所以还是spring中hibernate.current_session_context_class的问题

在spring的类LocalSessionFactoryBean源码,方法buildSessionFactory中将hibernate.current_session_context_class设为org.springframework.orm.hibernate3.SpringSessionContext


Java代码
if (isExposeTransactionAwareSessionFactory()) {
// Set Hibernate 3.1+ CurrentSessionContext implementation,
// providing the Spring-managed Session as current Session.
// Can be overridden by a custom value for the corresponding Hibernate property.
config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
}

if (this.jtaTransactionManager != null) {
// Set Spring-provided JTA TransactionManager as Hibernate property.
config.setProperty(Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName());
config.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
} else {
// Makes the Hibernate Session aware of the presence of a Spring-managed transaction.
// Also sets connection release mode to ON_CLOSE by default.
config.setProperty(Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName());
}

if (isExposeTransactionAwareSessionFactory()) {
// Set Hibernate 3.1+ CurrentSessionContext implementation,
// providing the Spring-managed Session as current Session.
// Can be overridden by a custom value for the corresponding Hibernate property.
config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
}

if (this.jtaTransactionManager != null) {
// Set Spring-provided JTA TransactionManager as Hibernate property.
config.setProperty(Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName());
config.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
} else {
// Makes the Hibernate Session aware of the presence of a Spring-managed transaction.
// Also sets connection release mode to ON_CLOSE by default.
config.setProperty(Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName());
}


知识总结:
hibernate.current_session_context_class属性
sessionFactory.getCurrentSession()可以完成一系列的工作,当调用时hibernate将session绑定到当前线程,事务结束后hibernate将session从当前线程中释放并且关闭session。当再次调用getCurrentSession()时将得到一个新的session,并重新开始这一系列工作。
这样调用方法如下:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit(); //不需要close session了。 前提是改值设置为了thread.

在一个应用程序中,如果DAO 层使用Spring 的hibernate 模板,通过Spring 来控制session 的生命周期,则首选getCurrentSession ()。
使用Hibernate的大多数应用程序需要某种形式的“上下文相关的” session,特定的session在整个特定的上下文范围内始终有效。然而,对不同类型的应用程序而言,要为什么是组成这种“上下文”下一个定义通常 是困难的;不同的上下文对“当前”这个概念定义了不同的范围。在3.0版本之前,使用Hibernate的程序要么采用自行编写的基于 ThreadLocal的上下文session,要么采用HibernateUtil这样的辅助类,要么采用第三方框架(比如Spring或Pico), 它们提供了基于代理(proxy)或者基于拦截器(interception)的上下文相关session。

从3.0.1版本开 始,Hibernate增加了SessionFactory.getCurrentSession()方法。一开始,它假定了采用JTA事务,JTA事务 定义了当前session的范围和上下文(scope and context)。Hibernate开发团队坚信,因为有好几个独立的JTA TransactionManager实现稳定可用,不论是否被部署到一个J2EE容器中,大多数(假若不是所有的)应用程序都应该采用JTA事务管理。 基于这一点,采用JTA的上下文相关session可以满足你一切需要。



更好的是,从3.1开始,SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,我们引入了新的扩展接口(org.hibernate.context.CurrentSessionContext)和新的配置参数(hibernate.current_session_context_class),以便对什么是“当前session”的范围和上下文(scope and context)的定义进行拔插。
请参阅org.hibernate.context.CurrentSessionContext接口的Javadoc,那里有关于它的契约的详细讨论。它定义了单一的方法,currentSession(),特定的实现用它来负责跟踪当前的上下文session。Hibernate内置了此接口的三种实现。
org.hibernate.context.JTASessionContext - 当前session根据JTA来跟踪和界定。这和以前的仅支持JTA的方法是完全一样的。详情请参阅Javadoc。
org.hibernate.context.ThreadLocalSessionContext - 当前session通过当前执行的线程来跟踪和界定。详情也请参阅Javadoc。
org.hibernate.context.ManagedSessionContext - 当前session通过当前执行的线程来跟踪和界定。但是,你需要负责使用这个类的静态方法将Session实例绑定、或者取消绑定,它并不会打开(open)、flush或者关闭(close)任何Session。
前两种实现都提供了“每数据库事务对应一个session”的编程模型,也称作每次请求一个session。Hibernate session的起始和终结由数据库事务的生存来控制。假若你在纯粹的 Java SE之上采用自行编写代码来管理事务,而不使用JTA,建议你使用Hibernate Transaction API来把底层事务实现从你的代码中隐藏掉。如果你使用JTA,请使用JTA借口来管理Transaction。如果你在支持CMT的EJB容器中执行代码,事务边界是声明式定义的,你不需要在代码中进行任何事务或session管理操作。
hibernate.current_session_context_class配置参数定义了应该采用哪个org.hibernate.context.CurrentSessionContext实现。注意,为了向下兼容,如果未配置此参数,但是存在org.hibernate.transaction.TransactionManagerLookup的配置,Hibernate会采用org.hibernate.context.JTASessionContext。一般而言,此参数的值指明了要使用的实现类的全名,但那三种内置的实现可以使用简写,即"jta"、"thread"和"managed"。

1、getCurrentSession()与openSession()的区别?
在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是CurrentSessionContext.currentSession() 。在 currentSession() 执行时,如果当前 Session 为空,currentSession 会调用SessionFactory的openSession 。所以getCurrentSession() 对于 Java EE 来说是更好的获取 Session 的方法。
* 采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession()创建的session则不会
* 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession()创建的session必须手动关闭
2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
* 如果使用的是本地事务(jdbc事务)
thread
织梦内容管理系统
* 如果使用的是全局事务(jta事务)
jta
* 如果使用的是session的管理机制(不太常用)
managed

利于ThreadLocal模式管理Session
早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序
时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,
而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)
其实的功用非常简单,就是为每一个使用某变量的线程都提供一个该变量值的副本,是每一个线程都可以独立地改变自己的副本,
而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,
用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):
public class HibernateUtil {
public static final ThreadLocal session =new ThreadLocal();
public static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session currentSession () throws HibernateException {
Session s = session.get ();
if(s == null) {
s = sessionFactory.openSession ();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = session.get ();
if(s != null) {
s.close();
}
session.set(null);
}
}

<session-factory> <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider, NHibernate</property> <!-- 每种drever_class对Oracle的性能不一样,OracleClientDriver对高并发性能支持很好;如果需要使用存储过程则必须要使用OracleManagedDataClientDriver; --> <!--<property name="connection.driver_class">NHibernate.Driver.OracleClientDriver</property>--> <!--<property name="connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>--> <property name="connection.driver_class">NHibernate.Driver.OracleManagedDataClientDriver</property> <property name="connection.connection_string"> <!-- User ID=fheap; Password=fheap; Data Source=(DESCRIPTION=(TRANSPORT_CONNECT_TIMEOUT = 60)(ADDRESS=(PROTOCOL=TCP)(HOST=10.255.1.13)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=10.255.1.14)(PORT=1521))(LOAD_BALANCE=yes)(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=cim)(FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRIES=180)(DELAY=5)))); Pooling=True;Max Pool Size = 100;Min Pool Size = 0;Connect Timeout=500;--> User ID=FHEAP; Password=fheap; Data Source=(DESCRIPTION=(TRANSPORT_CONNECT_TIMEOUT = 60)(ADDRESS=(PROTOCOL=TCP)(HOST=172.17.6.28)(PORT=1521))(LOAD_BALANCE=yes)(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=mesdbtest)(FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRIES=180)(DELAY=5)))); Pooling=True;Max Pool Size = 100;Min Pool Size = 0;Connect Timeout=500; <!--User ID=fheap; Password=123456; Data Source=(DESCRIPTION=(TRANSPORT_CONNECT_TIMEOUT = 60)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.85.128)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));Pooling=True;Max Pool Size = 150;Min Pool Size = 10;Connect Timeout=500;--> <!--User ID=mesforehope; Password=mesforehope; Data Source=(DESCRIPTION=(TRANSPORT_CONNECT_TIMEOUT = 60)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=10.255.1.34)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=mesdb)));--> </property> <property name="show_sql">false</property> <property name="dialect">NHibernate.Dialect.Oracle12cDialect</property> <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property> <!-- If your database setup use an ASCII charset, switch following property to true. --> <property name="oracle.use_n_prefixed_types_for_unicode">false</property> <!-- Depending on your database setup, the default cast length of 4000 may be too big. By example, if previous setting is true, NHibernate may try to use nvarchar2(4000), which will be rejected if its underlying charset is UTF16 and the database MAX_STRING_SIZE is not extended. In such case, reduce it to 2000. --> <property name="query.default_cast_length"></property> <property name="hbm2ddl.keywords">none</property> <!-- 每个线程获取到的session是不一样的。多线程并发一定要配置,否则会导致线程重用,获取一样的session,就会导致后来获取到的session失效 --> <property name="current_session_context_class">thread_static</property> <!--加载映射--> <mapping assembly="Nebula.EAP.Core.Scenario" /> </session-factory> 只更换驱动NHibernate.Driver.OracleClientDriver对其他配置有没有影响
最新发布
06-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值