Session并非线程安全
共享变量session实现Session的重用,避免了每次操作都要重新创建,但是多个线程同时使用一个Session实例进行数据存取,则将会导致Session 数据存取逻辑混乱
public class TestServlet extends HttpServlet {
private Session session;
public void doGet( HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
session = getSession();
doSomething();
session.flush();
}
public void doSomething(){
......//基于session的存取操作
}
}
不安全解释,例如:Servlet 运行是多线程的,而应用服务器并不会为每个线程都创建一个Servlet实例,也就是说,TestServlet在应用服务器中只有一个实例(在Tomcat中是这样,其他的应用服务器可能有不同的实现),而这个实例会被许多个线程并发调用,doGet 方法也将被不同的线程反复调用,可想而知,每次调用doGet 方法,这个唯一的TestServlet 实例的session 变量都会被重置
Hibernate对session的管理
hibernate提供了getCurrentSession 和openSession()来获取session ,首先看看两个方法的区别
1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭
3.getCurrentSession本地事务(本地事务:jdbc)时 要在配置文件里进行如下设置
* 如果使用的是本地事务(jdbc事务)
<property name="hibernate.current_session_context_class">thread</property>
* 如果使用的是全局事务(jta事务)
<property name="hibernate.current_session_context_class">jta</property>
4.getCurrentSession () 总是使用当前的session,openSession() 则重新建立一个新的session
在一个应用程序中,如果DAO 层使用Spring 的hibernate 模板,通过Spring 来控制session 的生命周期,则首选getCurrentSession ()。
用过Hibernate的人都知道Hibernate最原始的使用Session方式
获取SessionFactory 打开Session 打开事务(可选) 执行操作 关闭事务(可选) 关闭Session |
getCurrentSession() 这个方法就是通过SessionContext来减少Session创建的。比如常用的ThreadLocalSessionContext:
Session current = existingSession( factory );
if (current == null) {
current = buildOrObtainSession();
current.getTransaction().registerSynchronization( buildCleanupSynch() );
if ( needsWrapping( current ) ) {
current = wrap( current );
}
doBind( current, factory );
}
return current;
currentSession()内部先从ThreadLocal中获取Session。若不为null直接返回,若为null则openSession(...)一个Session并把这个Session对象绑定在ThreadLocal上。它就是通过在ThreadLocal中注册绑定Session来确保线程中最多只有一个Session对象。
在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本
public class ThreadLocal {
private Map values = Collections.synchronizedMap(new HashMap());
public Object get() {
Thread currentThread = Thread.currentThread();
Object result = values.get(currentThread); //通过currentThread来找result
if(result == null&&!values.containsKey(currentThread)) {
result = initialValue();
values.put(currentThread, result);
}
return result;
}
public void set(Object newValue) {
values.put(Thread.currentThread(), newValue);
}
public Object initialValue() {
return null;
}
}
Hibernate官方提供的一个ThreadLocal工具:
package cash.util;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Session;
import net.sf.hibernate.cfg.Configuration;
import org.apache.log4j.Logger;
/**
* Helper Singleton class to manage Hibernate Sessions.
*
* @author Joel Hockey
* @version $Id: HibernateUtil.java,v 1.24 2006/08/23 18:50:50 rainerh Exp $
*/
public class HibernateUtil {
/** ThreadLocal Session Map */
public static final ThreadLocal MAP = new ThreadLocal();
private static final Logger LOG = Logger.getLogger(HibernateUtil.class);
private static final SessionFactory SESSION_FACTORY;
/** Make default construct private */
private HibernateUtil() { }
/** Loads Hibernate config. */
static {
try {
LOG.debug("HibernateUtil.static - loading config");
SESSION_FACTORY = new Configuration().configure().buildSessionFactory();
LOG.debug("HibernateUtil.static - end");
} catch (HibernateException ex) {
throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), ex);
}
}
/**
* Gets Hibernate Session for current thread. When finished, users
* must return session using {@link #closeSession() closeSession()} method.
* @return Hibernate Session for current thread.
* @throws HibernateException if there is an error opening a new session.
*/
public static Session currentSession() throws HibernateException {
Session s = (Session)MAP.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = SESSION_FACTORY.openSession();
MAP.set(s);
}
return s;
}
/**
* Closes the Hibernate Session. Users must call this method after calling
* {@link #currentSession() currentSession()}.
* @throws HibernateException if session has problem closing.
*/
public static void closeSession() throws HibernateException {
Session s = (Session)MAP.get();
MAP.set(null);
if (s != null) {
s.close();
}
}
}
针对WEB程序,还可以利用Servlet2.3的Filter机制,轻松实现线程生命周期内的Session管理。下面是一个通过Filter进行Session管理的典型案例:
public class PersistenceFilter implements Filter {
protected static ThreadLocal hibernateHolder = new ThreadLocal();
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
hibernateHolder.set(getSession());
try {
chain.doFilter(request, response);
} finally {
Session session = (Session) hibernateHolder.get();
if (session != null) {
hibernateHolder.set(null);
try {
session.close();
} catch (HibernateException ex) {
throw new ServletException(ex);
}
}
}
}
}
在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。
这 两种实现都提供了“每数据库事务对应一个session”的编程模型,也称作session-per-request。Hibernate session的起始和终结由数据库事务的生存来控制。假若你采用自行编写代码来管理事务(比如,在纯粹的J2SE,或者 JTA/UserTransaction/BMT),建议你使用Hibernate Transaction API来把底层事务实现从你的代码中隐藏掉。如果你在支持CMT的EJB容器中执行,事务边界是声明式定义的,你不需要在代码中进行任何事务或 session管理操作。请参阅第 11 章 事务和并发一节来阅读更多的内容和示例代码。
hibernate.current_session_context_class 配置参数定义了应该采用哪个org.hibernate.context.CurrentSessionContext实现。注意,为了向下兼容,如果未 配置此参数,但是存在org.hibernate.transaction.TransactionManagerLookup的配 置,Hibernate会采用org.hibernate.context.JTASessionContext。一般而言,此参数的值指明了要使用的实 现类的全名,但那两个内置的实现可以使用简写,即"jta"和"thread"
sss