1)openSession和getCurrentSession的区别
openSession必须关闭,currentSession在事务结束后自动关闭
openSession没有和当前线程绑定,currentSession和当前线程绑定
2)如果使用currentSession需要在hibernate.cfg.xml文件中进行配置:
a、如果是本地事务(jdbc事务)
<propertyname="hibernate.current_session_context_class">thread</property>
b、如果是全局事务(jta事务)
<propertyname="hibernate.current_session_context_class">jta</property>
c、spring管理事务
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
</prop>
一般我们使用下面这种:
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
</prop>
由spring管理,因为通常我们都会使用 spring管理事务,如下
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
所以使用a、b来配置会出错。
全局事务:资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用XA 二阶段提交协议与“企业信息系统”(EIS) 或数据库进行交互。
本地事务:在单个 EIS或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。
上面这两种方式都是可以用sessionFactory . 点出来的。还有一种方式获得session,就是使用辅助类HibernateUtil这种:
package com.cxp.util;
import org.hibernate.*;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public final class HibernateUtil {
// 初始化一个ThreadLocal对象
private static final ThreadLocal sessionTL = new ThreadLocal();
private static Configuration configuration;
private final static SessionFactory sessionFactory;
static {
try {
// 加载配置文件hibernate.cfg.xml
configuration = new Configuration().configure();
// 获得会话工厂
// sessionFactory = configuration.buildSessionFactory();
// 这是Hibernate4.0之后引入的新特性,Service Register机制
StandardServiceRegistry ssrRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(ssrRegistry);
} catch (Throwable ex) {
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
/**
* 获取Session
*/
public static Session currentSession() {
// sessionTL的get()方法根据当前线程返回其对应的线程内部变量,
// 也就是我们需要的Session,多线程情况下共享数据库连接是不安全的。
// ThreadLocal保证了每个线程都有自己的Session。
Session session = (Session) sessionTL.get();
// 如果session为null,则打开一个新的session
if (session == null) {
// 创建一个数据库连接对象session。
session = sessionFactory.openSession();
// 保存该数据库连接session到ThreadLocal中。
sessionTL.set(session);
}
// 如果当前线程已经访问过数据库了,
// 则从sessionTL中get()就可以获取该线程上次获取过的数据库连接对象。
return session;
}
/**
* 关闭Session
*/
@SuppressWarnings("unchecked")
public static void closeSession() {
Session session = (Session) sessionTL.get();
sessionTL.set(null);
session.close();
}
}
其实这种方式获得的session和使用getCurrentSession获得的session是一样的都是和当前线程绑定的。
下面是spring管理事务时获取Session的源代码分析:
***1.我们点开sessionFactory的getCurrentSession()方法就会发现
sessionFactoryImpl是下面这样的:*
public Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
2.而按Ctrl再点击currentSession()时会发现其实现有四个(ssh环境下),其中最后一个是SpringSessionContext,它实现CurrentSessionContext这个接口,我们点击SpringSessionContext类下的currentSession() 方法:
public Session currentSession() throws HibernateException {
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
if (value instanceof Session) {
return (Session) value;
}
else if (value instanceof SessionHolder) {
SessionHolder sessionHolder = (SessionHolder) value;
Session session = sessionHolder.getSession();
if (!sessionHolder.isSynchronizedWithTransaction() &&
TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.equals(FlushMode.MANUAL) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
return session;
}
if (this.transactionManager != null) {
try {
if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
Session session = this.jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
}
return session;
}
}
catch (SystemException ex) {
throw new HibernateException("JTA TransactionManager found but status check failed", ex);
}
}
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Session session = this.sessionFactory.openSession();
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
sessionHolder.setSynchronizedWithTransaction(true);
return session;
}
else {
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
}
}
}
3.先不去看其它的,这个方法最终都是返回Session,那它是怎么获得Session的呢,通过下面这句:
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
关键是getResource(this.sessionFactory)这个方法,点开来看:
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
4.找到关键的方法doGetResource(actualKey),继续点开:
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.get(actualKey);
// Transparently remove ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
5.这个resources是个 NamedThreadLocal 对象,而其继承于ThreadLocal,其get()方法为:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
可以看到,其调用的就是ThreadLocal的get()方法,所以其实跟用HibernateUtil时用来绑定当前线程的方式是一样的。