`
xinglijun1973
  • 浏览: 51908 次
社区版块
存档分类
最新评论

提前关闭open session in view 模式下的数据库连接

阅读更多

背景:

   open session in  view为 懒加载提供了在控制层或者freemarker页面动态访问迟加载数据的功能。但缺点也是显而易见的,那就是 要将页面全部写到浏览器后,数据库连接才能关闭,这对数据库这样的稀缺资源来说,是不可忍受的。那么能否在freemarker等view层获取完毕迟加载的数据后,立即关闭数据库连接呢?

 

方案:

    freemarker模板空写一遍后,模板里的参数已经加载好,提前关闭session。

具体步骤:

    1)将struts-plugin.xml里面ftl的类 改为 :
<bean type="com.skyon.webframe.core.struts2.ResultProxy" name="ftl" class="com.skyon.opensessioniv.FreemarkerResultProxy" />
 这个新的 FreemarkerResultProxy返回新的Result,Result将模板空写一遍:

 

						nullWrite(template, model);
						template.process(model, parentCharArrayWriter);

 然后关闭连接: 

 

	// 空写一遍模板,并提前关闭OpenSessionInViewAnyCloseFilter的session。这样模板里面的参数将被访问一遍以确保里面的参数可用。
	private void nullWrite(Template template, TemplateModel model) throws TemplateException, IOException {

		template.process(model, NullWriter.NULL_WRITER);
		LOG.debug("nullWrite template ok.");
		OpenSessionInViewAnyCloseFilter instance = OpenSessionInViewAnyCloseFilter.getInstance();
		if(instance!=null && instance.isInFilter()) {
			instance.closeSession();
			LOG.debug("OpenSessionInViewAnyCloseFilter.close() ok.");
		}else {
			LOG.debug("OpenSessionInViewAnyCloseFilter.instance is null.");
		}
	}

 

 

 

2)将web.xml的filter部分改为:
 

<filter>
   <filter-name>opensession</filter-name>
   <filter-class>com.skyon.opensessioniv.OpenSessionInViewAnyCloseFilter</filter-class>
   <init-param>
    <param-name>singleSession</param-name>
    <param-value>false</param-value>
   </init-param> 
  </filter>   
  <filter>
      其他filter
  </filter>
  
  <filter-mapping>
   <filter-name>opensession</filter-name>
   <url-pattern>/params/*</url-pattern>
  </filter-mapping>
  <!-- opensession的多个url-pattern 写多个<filter-mapping> -->
  <!-- <filter-mapping>
   <filter-name>opensession</filter-name>
   <url-pattern>/params/dict/*</url-pattern>
  </filter-mapping> -->

  

  注意:1)opensessioninview 一定要放在最前面
             2)要加 url-pattern,否则每次请求(比如js,图片请求)都要开关session。
 而这个新的 opensessioninview filer,提供了提前关闭数据库连接的方法 closeSession():

/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.skyon.opensessioniv;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * Servlet 2.3 Filter that binds a Hibernate Session to the thread for the
 * entire processing of the request. Intended for the "Open Session in View"
 * pattern, i.e. to allow for lazy loading in web views despite the original
 * transactions already being completed.
 *
 * <p>
 * This filter makes Hibernate Sessions available via the current thread, which
 * will be autodetected by transaction managers. It is suitable for service
 * layer transactions via
 * {@link org.springframework.orm.hibernate3.HibernateTransactionManager} or
 * {@link org.springframework.transaction.jta.JtaTransactionManager} as well as
 * for non-transactional execution (if configured appropriately).
 *
 * <p>
 * <b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate
 * Session, with the flush mode set to <code>FlushMode.NEVER</code>. It assumes
 * to be used in combination with service layer transactions that care for the
 * flushing: The active transaction manager will temporarily change the flush
 * mode to <code>FlushMode.AUTO</code> during a read-write transaction, with the
 * flush mode reset to <code>FlushMode.NEVER</code> at the end of each
 * transaction. If you intend to use this filter without transactions, consider
 * changing the default flush mode (through the "flushMode" property).
 *
 * <p>
 * <b>WARNING:</b> Applying this filter to existing logic can cause issues that
 * have not appeared before, through the use of a single Hibernate Session for
 * the processing of an entire request. In particular, the reassociation of
 * persistent objects with a Hibernate Session has to occur at the very
 * beginning of request processing, to avoid clashes with already loaded
 * instances of the same objects.
 *
 * <p>
 * Alternatively, turn this filter into deferred close mode, by specifying
 * "singleSession"="false": It will not use a single session per request then,
 * but rather let each data access operation or transaction use its own session
 * (like without Open Session in View). Each of those sessions will be
 * registered for deferred close, though, actually processed at request
 * completion.
 *
 * <p>
 * A single session per request allows for most efficient first-level caching,
 * but can cause side effects, for example on <code>saveOrUpdate</code> or when
 * continuing after a rolled-back transaction. The deferred close strategy is as
 * safe as no Open Session in View in that respect, while still allowing for
 * lazy loading in views (but not providing a first-level cache for the entire
 * request).
 *
 * <p>
 * Looks up the SessionFactory in Spring's root web application context.
 * Supports a "sessionFactoryBeanName" filter init-param in
 * <code>web.xml</code>; the default bean name is "sessionFactory". Looks up the
 * SessionFactory on each request, to avoid initialization order issues (when
 * using ContextLoaderServlet, the root application context will get initialized
 * <i>after</i> this filter).
 * <p>
 * 在原来基础上,加了 提前关闭session的功能。比如在使用模板情况下,模板参数已经准备好,还整个页面没向浏览器输出的时候,关闭session。
 * 
 * @author Juergen Hoeller
 * @author xinglj
 * 
 * @since 1.2
 * @see #setSingleSession
 * @see #setFlushMode
 * @see #lookupSessionFactory
 * @see OpenSessionInViewInterceptor
 * @see org.springframework.orm.hibernate3.HibernateInterceptor
 * @see org.springframework.orm.hibernate3.HibernateTransactionManager
 * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
 * @see org.springframework.transaction.support.TransactionSynchronizationManager
 */
public class OpenSessionInViewAnyCloseFilter extends OncePerRequestFilter {

	public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";

	private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;

	private boolean singleSession = true;

	private FlushMode flushMode = FlushMode.MANUAL;

	private static OpenSessionInViewAnyCloseFilter instance;

	public static OpenSessionInViewAnyCloseFilter getInstance() {
		if (instance == null)
			throw new RuntimeException("it's null,pls call it later!");
		else
			return instance;
	}

	// 是否经过了此filter。处理外部主动调用close session,但并没有session的情况。
	private ThreadLocal<Boolean> inFilter = new ThreadLocal<Boolean>() {
		protected Boolean initialValue() {
			return Boolean.FALSE;
		}
	};

	// session是否已经关闭。防止重复关闭
	private ThreadLocal<Boolean> sessionClosed = new ThreadLocal<Boolean>() {
		protected Boolean initialValue() {
			return Boolean.FALSE;
		}
	};

	public OpenSessionInViewAnyCloseFilter() {
		if (instance != null)
			throw new RuntimeException("pls new it only once!");
		else {
			instance = this;
		}
	}

	/**
	 * Set the bean name of the SessionFactory to fetch from Spring's root
	 * application context. Default is "sessionFactory".
	 * 
	 * @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
	 */
	public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
		this.sessionFactoryBeanName = sessionFactoryBeanName;
	}

	/**
	 * Return the bean name of the SessionFactory to fetch from Spring's root
	 * application context.
	 */
	protected String getSessionFactoryBeanName() {
		return this.sessionFactoryBeanName;
	}

	/**
	 * Set whether to use a single session for each request. Default is "true".
	 * <p>
	 * If set to "false", each data access operation or transaction will use its own
	 * session (like without Open Session in View). Each of those sessions will be
	 * registered for deferred close, though, actually processed at request
	 * completion.
	 * 
	 * @see SessionFactoryUtils#initDeferredClose
	 * @see SessionFactoryUtils#processDeferredClose
	 */
	public void setSingleSession(boolean singleSession) {
		this.singleSession = singleSession;
	}

	/**
	 * Return whether to use a single session for each request.
	 */
	protected boolean isSingleSession() {
		return this.singleSession;
	}

	/**
	 * Specify the Hibernate FlushMode to apply to this filter's
	 * {@link org.hibernate.Session}. Only applied in single session mode.
	 * <p>
	 * Can be populated with the corresponding constant name in XML bean
	 * definitions: e.g. "AUTO".
	 * <p>
	 * The default is "MANUAL". Specify "AUTO" if you intend to use this filter
	 * without service layer transactions.
	 * 
	 * @see org.hibernate.Session#setFlushMode
	 * @see org.hibernate.FlushMode#MANUAL
	 * @see org.hibernate.FlushMode#AUTO
	 */
	public void setFlushMode(FlushMode flushMode) {
		this.flushMode = flushMode;
	}

	/**
	 * Return the Hibernate FlushMode that this filter applies to its
	 * {@link org.hibernate.Session} (in single session mode).
	 */
	protected FlushMode getFlushMode() {
		return this.flushMode;
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		inFilter.set(Boolean.TRUE);
		sessionClosed.set(Boolean.FALSE);
		
		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 &&!sessionClosed.get()) {
				closeSessionIfNeccesary(sessionFactory);
			}
		}
	}
	/**
	 * 是否经过了此filter
	 * @return
	 */
	public Boolean isInFilter() {
		return inFilter.get();
	}
	/**
	 * close thread-bound session
	 */
	public void closeSession() {
		
			closeSessionIfNeccesary(lookupSessionFactory());
	}

	private void closeSessionIfNeccesary(SessionFactory sessionFactory) {
		if (!sessionClosed.get()) {
			logger.debug("close session...");
			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);
			}
			sessionClosed.set(Boolean.TRUE);
		} else {
			logger.debug("has closed session,do nothing.");
		}
	}

	/**
	 * Look up the SessionFactory that this filter should use, taking the current
	 * HTTP request as argument.
	 * <p>
	 * The default implementation delegates to the {@link #lookupSessionFactory()}
	 * variant without arguments.
	 * 
	 * @param request
	 *            the current request
	 * @return the SessionFactory to use
	 */
	protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
		return lookupSessionFactory();
	}

	/**
	 * Look up the SessionFactory that this filter should use.
	 * <p>
	 * The default implementation looks for a bean with the specified name in
	 * Spring's root application context.
	 * 
	 * @return the SessionFactory to use
	 * @see #getSessionFactoryBeanName
	 */
	protected SessionFactory lookupSessionFactory() {
		if (logger.isDebugEnabled()) {
			logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
		}
		WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
		return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
	}

	/**
	 * Get a Session for the SessionFactory that this filter uses. Note that this
	 * just applies in single session mode!
	 * <p>
	 * The default implementation delegates to the
	 * <code>SessionFactoryUtils.getSession</code> method and sets the
	 * <code>Session</code>'s flush mode to "MANUAL".
	 * <p>
	 * Can be overridden in subclasses for creating a Session with a custom entity
	 * interceptor or JDBC exception translator.
	 * 
	 * @param sessionFactory
	 *            the SessionFactory that this filter uses
	 * @return the Session to use
	 * @throws DataAccessResourceFailureException
	 *             if the Session could not be created
	 * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory,
	 *      boolean)
	 * @see org.hibernate.FlushMode#MANUAL
	 */
	protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
		Session session = SessionFactoryUtils.getSession(sessionFactory, true);
		FlushMode flushMode = getFlushMode();
		if (flushMode != null) {
			session.setFlushMode(flushMode);
		}
		return session;
	}

	/**
	 * Close the given Session. Note that this just applies in single session mode!
	 * <p>
	 * Can be overridden in subclasses, e.g. for flushing the Session before closing
	 * it. See class-level javadoc for a discussion of flush handling. Note that you
	 * should also override getSession accordingly, to set the flush mode to
	 * something else than NEVER.
	 * 
	 * @param session
	 *            the Session used for filtering
	 * @param sessionFactory
	 *            the SessionFactory that this filter uses
	 */
	protected void closeSession(Session session, SessionFactory sessionFactory) {
		SessionFactoryUtils.closeSession(session);
	}

}

 

 

分享到:
评论

相关推荐

    asp连接数据库代码实例

    连接数据库代码实例 1,连接数据库代码 文件名称 conn.asp 所有访问数据库的文件都调用此文件&lt;!--#include file=\"Conn.asp\"--&gt; db=\"data/data.mdb\" \'数据库存放目录 on error resume next set conn=server...

    spring-jpa-wicket-bootstrap:使用 Spring、JPA、Hibernate、Wicket 和 Bootstrap 的 J2EE Web 模板。 在 Tomcat 和 Postgres DB 上测试

    它演示了MvC 、 SoC 、 IoC 、 DAO 、 Service layer和Open Session in View模式以及其他 J2EE 最佳实践。 该模板可以轻松扩展为功能齐全的基于 Wicket 的 Web 应用程序。 用法 该模板使用 maven 并在 Tomcat7 上...

    mysql数据库的基本操作语法

    索引是存放在模式(schema)中的一个数据库对象,索引的作用就是提高对表的检索查询速度, 索引是通过快速访问的方法来进行快速定位数据,从而减少了对磁盘的读写操作。 索引是数据库的一个对象,它不能独立存在,...

    orcale常用命令

    当不能关闭数据库时,可以用startup force来完成数据库的关闭 先关闭数据库,再执行正常启动数据库命令 7、startup pfile=参数文件名 带初始化参数文件的启动方式 先读取参数文件,再按参数文件中的设置启动数据库...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    1、 Struts是一个为开发基于模型(Model)-视图(View)-控制器(Controller)(MVC)模式的应用架构的开源框架,是利用Servlet,JSP和custom tag library构建Web应用的一项非常有用的技术。由于Struts能充分满足应用开发...

    Oracle8i_9i数据库基础

    §4.2 实体视图(MATERIALIZED VIEW) 131 §4.2.1 创建实体视图 131 §4.2.2 创建实体视图日志 137 §4.2.3 修改实体视图 139 §4.2.4 修改实体视图日志 141 §4.2.45 实体视图完整例子 142 §4.3 序号(sequence) 146...

    数据库基础

    §4.2 实体视图(MATERIALIZED VIEW) 131 §4.2.1 创建实体视图 131 §4.2.2 创建实体视图日志 137 §4.2.3 修改实体视图 139 §4.2.4 修改实体视图日志 141 §4.2.45 实体视图完整例子 142 §4.3 序号(sequence) 146...

    最全的oracle常用命令大全.txt

    当不能关闭数据库时,可以用startup force来完成数据库的关闭 先关闭数据库,再执行正常启动数据库命令 7、startup pfile=参数文件名 带初始化参数文件的启动方式 先读取参数文件,再按参数文件中的设置启动数据库...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    PostgreSQL 号称“世界上最先进的开源数据库“,可以运行在多种平台下,是tb级数据库,而且性能也很好 中大型企业 oracle 甲骨文 获得最高认证级别的ISO标准安全认证,性能最高, 保持开放平台下的TPC-D和TPC-C的...

    asp.net知识库

    2分法-通用存储过程分页(top max模式)版本(性能相对之前的not in版本极大提高) 分页存储过程:排序反转分页法 优化后的通用分页存储过程 sql语句 一些Select检索高级用法 SQL server 2005中新增的排序函数及应用 ...

    java开源包1

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    CuteFTP9简易汉化版

    SSL会话Choices-When设置SSL连接,一种上传软件允许您选择三种常见的SSL实现,包括TLS(AUTH TLS)*,SSL隐* *(直接连接在端口990)和SSL显式* *(身份验证SSL)模式。大多数FTP服务器支持至少一种,而一些(比如Globalscape ...

    Oracle9i的init.ora参数中文说明

    如果要在没有调度程序的情况下仍能连接到数据库, 请将该值设置为与例程名相同。此参数自 8.1.3 版起已废弃。 值范围: 根据操作系统而定。 默认值 :0 mts_sessions: 说明 : 指定允许的共享服务器体系结构用户会话的...

    springmybatis

    1.Configuration.xml 是 mybatis 用来建立 sessionFactory 用的,里面主要包含了数据库连接相关东西,还有 java 类所对应的别名,比如 &lt;typeAlias alias="User" type="com.yihaomen.mybatis.model.User"/&gt; 这个别名...

    超级有影响力霸气的Java面试题大全文档

     Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常...

    java开源包4

    它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K(运行时需要slf4j和guava的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。 异步...

    java 面试题 总结

    SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。 对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上...

    travelibrary-微信小程序实战-流动图书馆.zip

    分为两块视图层(View)和逻辑层(App Service)Flex:flex弹性布局Express : http服务框架websocket: 前后端消息的实时推送mongoose: 操作mongodb数据库pm2: 服务端使用pm2部署,常驻进程截图首页借阅书架发布的图书...

    网管教程 从入门到精通软件篇.txt

     如果不带任何参数,diskpart 命令将启动 diskpart 的 Windows 字符模式版本。  /add  创建新的分区。  /delete  删除现有分区。  device_name  要创建或删除分区的设备。设备名称可从 map 命令的输出...

    JAVA上百实例源码以及开源项目源代码

    发送消息,同时对文本进行少量修改,发送end-of-messages消息,最后关闭连接。 Tcp服务端与客户端的JAVA实例源代码 2个目标文件 摘要:Java源码,文件操作,TCP,服务器  Tcp服务端与客户端的JAVA实例源代码,一个简单...

Global site tag (gtag.js) - Google Analytics