OpenSessionInViewFilter在网上已经有很多分析资料了,看人家写得那么好,心里痒痒的。于是决定自己也写一篇
protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { SessionFactory sessionFactory = lookupSessionFactory(request); boolean participate = false; if (isSingleSession()) { // single session mode if (TransactionSynchronizationManager.hasResource(sessionFactory)) { // Do not modify the Session: just set the participate flag. participate = true; } else { logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter"); Session session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } } else { // deferred close mode if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { // Do not modify deferred close: just set the participate flag. participate = true; } else { SessionFactoryUtils.initDeferredClose(sessionFactory); } } try { filterChain.doFilter(request, response); } finally { if (!participate) { if (isSingleSession()) { // single session mode SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter"); closeSession(sessionHolder.getSession(), sessionFactory); } else { // deferred close mode SessionFactoryUtils.processDeferredClose(sessionFactory); } } } }
这里面需要了解几个要点:
1.isSingleSession() 这个默认是true,代表使用OpenSessionInViewFilter
2.TransactionSynchronizationManager.hasResource(sessionFactory)
TransactionSynchronizationManager这个类中有一属性
private static final ThreadLocal resources = new ThreadLocal();
resources 里面存放的是map,而这个map里面存放的是sessionFactory和SessionHolder
public static void bindResource(Object key, Object value) throws IllegalStateException { Assert.notNull(key, "Key must not be null"); Assert.notNull(value, "Value must not be null"); Map map = (Map) resources.get(); // set ThreadLocal Map if none found if (map == null) { map = new HashMap(); resources.set(map); } if (map.containsKey(key)) { throw new IllegalStateException("Already value [" + map.get(key) + "] for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]"); } map.put(key, value); if (logger.isDebugEnabled()) { logger.debug("Bound value [" + value + "] for key [" + key + "] to thread [" + Thread.currentThread().getName() + "]"); } }
3.再接着就是比较重要的方法
Session session = getSession(sessionFactory);
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); FlushMode flushMode = getFlushMode(); if (flushMode != null) { session.setFlushMode(flushMode); } return session; }
因为有属性:private FlushMode flushMode = FlushMode.NEVER;
session.setFlushMode(flushMode);
所以OpenSessionInViewFilter默认的FlushMode 是NEVER
private static Session doGetSession( SessionFactory sessionFactory, Interceptor entityInterceptor, SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) throws HibernateException, IllegalStateException { Assert.notNull(sessionFactory, "No SessionFactory specified"); SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if (sessionHolder != null && !sessionHolder.isEmpty()) { // pre-bound Hibernate Session Session session = null; if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) { // Spring transaction management is active -> // register pre-bound Session with it for transactional flushing. session = sessionHolder.getValidatedSession(); if (session != null && !sessionHolder.isSynchronizedWithTransaction()) { logger.debug("Registering Spring transaction synchronization for existing Hibernate Session"); TransactionSynchronizationManager.registerSynchronization( new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false)); sessionHolder.setSynchronizedWithTransaction(true); // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session // with FlushMode.NEVER, which needs to allow flushing within the transaction. FlushMode flushMode = session.getFlushMode(); if (flushMode.lessThan(FlushMode.COMMIT) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { session.setFlushMode(FlushMode.AUTO); sessionHolder.setPreviousFlushMode(flushMode); } } } else { // No Spring transaction management active -> try JTA transaction synchronization. session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator); } if (session != null) { return session; } } logger.debug("Opening Hibernate Session"); Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession()); // Use same Session for further Hibernate actions within the transaction. // Thread object will get removed by synchronization at transaction completion. if (TransactionSynchronizationManager.isSynchronizationActive()) { // We're within a Spring-managed transaction, possibly from JtaTransactionManager. logger.debug("Registering Spring transaction synchronization for new Hibernate Session"); SessionHolder holderToUse = sessionHolder; if (holderToUse == null) { holderToUse = new SessionHolder(session); } else { holderToUse.addSession(session); } if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { session.setFlushMode(FlushMode.NEVER); } TransactionSynchronizationManager.registerSynchronization( new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != sessionHolder) { TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse); } } else { // No Spring transaction management active -> try JTA transaction synchronization. registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder); } // Check whether we are allowed to return the Session. if (!allowCreate && !isSessionTransactional(session, sessionFactory)) { closeSession(session); throw new IllegalStateException("No Hibernate Session bound to thread, " + "and configuration does not allow creation of non-transactional one here"); } return session; }
if (sessionHolder != null && !sessionHolder.isEmpty()) 里面的内容感觉只有在多线程并发时才会触发
这里在介绍一个类SessionHolder,这个类用于包装session和transaction
接下来谈下事务和session的关系吧
首先看看Hibernate中对事务的一些方法的封装吧
public void begin() throws HibernateException { if (begun) { return; } if (commitFailed) { throw new TransactionException("cannot re-start transaction after failed commit"); } log.debug("begin"); try { toggleAutoCommit = jdbcContext.connection().getAutoCommit(); if ( log.isDebugEnabled() ) { log.debug("current autocommit status: " + toggleAutoCommit); } if (toggleAutoCommit) { log.debug("disabling autocommit"); jdbcContext.connection().setAutoCommit(false); } } catch (SQLException e) { log.error("JDBC begin failed", e); throw new TransactionException("JDBC begin failed: ", e); } callback = jdbcContext.registerCallbackIfNecessary(); begun = true; committed = false; rolledBack = false; if ( timeout>0 ) { jdbcContext.getConnectionManager() .getBatcher() .setTransactionTimeout(timeout); } jdbcContext.afterTransactionBegin(this); }
jdbcContext.connection()是获取当前数据库的一个连接,大家可以看看c3p0的实现
public class C3P0ConnectionProvider implements ConnectionProvider { private DataSource ds; private Integer isolation; private boolean autocommit; private static final Log log = LogFactory.getLog(C3P0ConnectionProvider.class); public Connection getConnection() throws SQLException { final Connection c = ds.getConnection(); if (isolation!=null) c.setTransactionIsolation( isolation.intValue() ); if ( c.getAutoCommit()!=autocommit ) c.setAutoCommit(autocommit); return c; }
最后了解一个东东
Session.setFlushMode()用于设定清理缓存的时间点:
清理缓存的模式 Session的查询方法 Session.commit() Session.flush() FlushMode.AUTO 清理清理清理 FlushMode.COMMIT 不清理清理清理 FlushMode.NEVER 不清理不清理清