过滤器之Hibernate自动提交事务,并关闭会话

来源:互联网 发布:谷歌人工智能产品 编辑:程序博客网 时间:2024/06/08 23:50

      最近我们学到的Hibernate中,有个对房屋信息的查询功能。其中页面的一句代码如下:

   ${house.street.district.name}区${house.street.name},${house.floorage}平米<br /> 联系方式:${house.contact}

   以上写法用EL显示房屋的信息,但我们的DAO用Hibernate实现的时候,几个相关的对象都要把lazy设置成false,不然就会出现会话已经关闭的错误。以下是House.hbm.xml原来的写法:

  

        <many-to-one name="users" class="org.newboy.entity.Users" fetch="join" lazy="false">            <column name="USER_ID" precision="7" scale="0" />        </many-to-one>        <many-to-one name="houseType" class="org.newboy.entity.HouseType" fetch="select" lazy="false">            <column name="TYPE_ID" precision="4" scale="0" />        </many-to-one>        <many-to-one name="street" class="org.newboy.entity.Street" fetch="join" lazy="false">            <column name="STREET_ID" precision="6" scale="0" />        </many-to-one>

因为在访问到house.street的时候,Hibernate还会再去查一次数据库,而如果我们的DAO中的代码如下:

@SuppressWarnings("unchecked")public List<House> getAllHouses() {Session session = super.getSession();List<House> houses = null;try {houses = session.createQuery("from House").list();} finally {session.close();}return houses;}

会话已经关闭,就会出现会话关闭的错误。当然还有一种做法就是不关闭会话,不过这不是一种解决的办法。

虽然以后我们会学到Spring有更好的解决方法,但今天如果只用Hibernate的话,我们自己有没有办法去解决这个问题,就是让会话在请求结束后才关闭。这样也就不用把lazy设置成false了。于是就有了下面的过滤器的实现:


package org.newboy.servlet;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.apache.log4j.Logger;import org.hibernate.Session;import org.hibernate.Transaction;import org.newboy.utils.HibernateUtils;/** * 用于进行Hibernate事务处理的Servlet过滤器 *  * @author LiuBo */public class HibernateFilter implements Filter {private static Logger log = Logger.getLogger(HibernateFilter.class);/** * 过滤器的主要方法 用于实现Hibernate事务的开始和提交 */public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {Session session = HibernateUtils.getSession();Transaction tx = session.beginTransaction();try {// 开始一个新的事务log.info("Starting a database transaction");tx.begin();log.info("Request Path:\t" + ((HttpServletRequest) request).getServletPath());// Call the next filter (continue request processing)chain.doFilter(request, response);// 提交事务log.info("Committing the database transaction");tx.commit();} catch (Throwable ex) {ex.printStackTrace();try {// 回滚事务log.info("Trying to rollback database transaction after exception");tx.rollback();} catch (Throwable rbEx) {log.error("Could not rollback transaction after exception!", rbEx);}// 抛出异常throw new ServletException(ex);} finally {log.info("session closed");HibernateUtils.closeSession();}}/** * Servlet过滤器的初始化方法 可以读取配置文件中设置的配置参数 */public void init(FilterConfig filterConfig) throws ServletException {}/** * Servlet的销毁方法 用于释放过滤器所申请的资源 */public void destroy() {}}


在web.xml中配置如下:

<filter><description>事务的过滤器</description><filter-name>HibernateFilter</filter-name><filter-class>org.newboy.servlet.HibernateFilter</filter-class></filter><filter-mapping><filter-name>HibernateFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

这样我们的DAO就可以这样写:

@SuppressWarnings("unchecked")public List<House> getAllHouses() {Session session = HibernateUtils.getSession();List<House> houses = session.createQuery("from House").list();return houses;}

不用关闭会话,而House.hbm.xml就变成:

       <many-to-one name="users" class="org.newboy.entity.Users">            <column name="USER_ID" precision="7" scale="0" />        </many-to-one>        <many-to-one name="houseType" class="org.newboy.entity.HouseType">            <column name="TYPE_ID" precision="4" scale="0" />        </many-to-one>        <many-to-one name="street" class="org.newboy.entity.Street">            <column name="STREET_ID" precision="6" scale="0" />        </many-to-one>

没有了lazy那项。

其中HibernateUtils的代码基本上是MyEclipse自动产生的。只是我把它汉化了。呵呵:

package org.newboy.utils;import org.hibernate.HibernateException;import org.hibernate.Session;import org.hibernate.cfg.Configuration;/** * 配置Hibernate工具,对会话工厂和会话进行操作 */public class HibernateUtils {private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();private static Configuration configuration = new Configuration();private static org.hibernate.SessionFactory sessionFactory;private static String configFile = CONFIG_FILE_LOCATION;static {try {configuration.configure(configFile);sessionFactory = configuration.buildSessionFactory();} catch (Exception e) {System.err.println("会话工厂创建失败");e.printStackTrace();}}/* * 私有的构造方法 */private HibernateUtils() {}/** * 得到一个线程安全的实例 *  * @return Session * @throws HibernateException */public static Session getSession() throws HibernateException {Session session = (Session) threadLocal.get();if (session == null || !session.isOpen()) {if (sessionFactory == null) {rebuildSessionFactory();}session = (sessionFactory != null) ? sessionFactory.openSession() : null;threadLocal.set(session);}return session;}/** * 重新建立会话工厂 */public static void rebuildSessionFactory() {try {configuration.configure(configFile);sessionFactory = configuration.buildSessionFactory();} catch (Exception e) {System.err.println("会话工厂创建失败");e.printStackTrace();}}/** * 关闭单个会话实例. *  * @throws HibernateException */public static void closeSession() throws HibernateException {Session session = (Session) threadLocal.get();threadLocal.set(null);if (session != null) {session.close();}}/** * 返回会话工厂实例 */public static org.hibernate.SessionFactory getSessionFactory() {return sessionFactory;}/** * 设置配置文件 */public static void setConfigFile(String configFile) {HibernateUtils.configFile = configFile;sessionFactory = null;}/** * 返回配置对象 */public static Configuration getConfiguration() {return configuration;}}

这样每次用户请求就会打开会话和事务,请求结束就提交事务,并关闭会话。解决了上面的问题。