【问题标题】:When and where should I get a new session from hibernate 4.3.8.Final in a Spring MVC based Application?在基于 Spring MVC 的应用程序中,我应该何时何地从休眠 4.3.8.Final 获得一个新会话?
【发布时间】:2015-04-29 15:11:49
【问题描述】:

我已经为我的 Spring 应用程序配置了 Hibernate。我有一个名为 hibernateUtil.java 的文件,它为 Hibernate 创建了一个新会话。问题是我什么时候应该调用它的getSession 方法?有没有更好的方法来解决这个问题?

Hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/mydb
        </property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">12</property>

        <!-- SQL dialect -->
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>

        <property name="current_session_context_class">thread</property>


        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property>

        <mapping class="com.myproject.model.business" />




    </session-factory>

</hibernate-configuration>

HibernateUtil.java

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {

    private static ServiceRegistry serviceRegistry;
    private static final ThreadLocal<Session> threadLocal = new ThreadLocal();
    private static SessionFactory sessionFactory;


    private static SessionFactory configureSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.configure();
            serviceRegistry = new StandardServiceRegistryBuilder()
                    .applySettings(configuration.getProperties()).build();
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);
            return sessionFactory;
        } catch (HibernateException e) {
            e.printStackTrace();
        }
        return sessionFactory;
    }

    static {
        try {
            sessionFactory = configureSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private HibernateUtil() {
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static Session getSession() throws HibernateException {
        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 {
            sessionFactory = configureSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);

        if (session != null) {
            if (session.isOpen()) {
                session.close();
            }
        }
    }
}

我也发现了这个tutorial,但我不确定它是否可靠。建议使用 Spring bean 配置文件进行 Hibernate 配置。

【问题讨论】:

  • 在控制器方法中使用HibernateUtil.getSession() 的目的是什么?您的控制器代码中没有任何内容需要数据库交互,那么您为什么要获得会话呢?推荐的模式是基于每个 HTTP 请求创建会话。这在Hibernate documentation 中有解释。
  • @manish 我更新了问题,我只是把它放在控制器中以显示问题没有具体原因。
  • @manish 表示我不知道应该在何时何地调用 getsession 方法。
  • 看看 Spring ORM 模块中的OpenSessionInViewFilter。由于您已经在使用 Spring,您可以考虑将 Spring ORM 模块添加到您的应用程序中,摆脱您的自定义 HibernateUtils 类并让 Spring 管理您的 Hibernate 会话。
  • 您正在使用 Spring,然后让 Spring 为您完成困难的工作。放弃 HibernateUtil 类,让 spring 担心打开和关闭会话,这将为您省去麻烦并真正简化您的代码。我强烈建议阅读Spring reference guide

标签: java spring hibernate spring-mvc configuration


【解决方案1】:

不确定什么是rebuildSessionFactory() 方法。 SessionFactory 是单个数据存储的 Hibernates 概念,并且是线程安全的,因此许多线程可以同时访问它并请求会话和单个数据库的已编译映射的不可变缓存。 SessionFactory 通常只在启动时构建一次

会话是一种 Hibernate 构造,用于调解与数据库的连接。 会话在创建时打开单个数据库连接,并一直保持到会话关闭。 Hibernate 从数据库中加载的每个对象都与会话相关联,允许 Hibernate 自动持久化被修改的对象,并允许 Hibernate 实现延迟加载等功能。

public class HibernateUtil { 

    public static final ThreadLocal local = new ThreadLocal(); 

    public static Session currentSession() throws HibernateException { 
       Session session = (Session) local.get(); 
       //open a new session if this thread has no session 
       if(session == null) { 
          session = sessionFactory.openSession(); 
          local.set(session);     
       } 
      return session; 
   } 
} 

另外请检查分离、持久和瞬态对象之间的区别。不确定为什么要在 Controller 中打开休眠会话。

【讨论】:

  • 更新了问题,没有在控制器中有该代码的具体原因,我只是想举个例子。看起来不是一个好的。我知道事务和会话之间应该存在一对一的关系。我所做的是在创建任何事务之前我首先检查是否已经创建了任何会话,如果它是然后使用该会话,否则创建一个新会话。你对此有什么想法github.com/spring-projects/spring-framework/blob/master/…
【解决方案2】:

您应该在需要交易的时间(和地点)获得会话。这个想法是您需要一个事务会话。并且会话不是线程安全的,每个线程或事务都应该获得自己的实例。

话虽如此,如果您在 spring webapp 中使用容器管理的持久性,Spring 或 JPA 注释可以为您注入。

以下代码来自https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html

典型的交易应该使用以下成语:

Session sess = factory.openSession();
 Transaction tx;
 try {
     tx = sess.beginTransaction();
     //do some work
     ...
     tx.commit();
 }
 catch (Exception e) {
     if (tx!=null) tx.rollback();
     throw e;
 }
 finally {
     sess.close();
 }

【讨论】:

  • 没错,但它总是在创建一个新会话。问题是创建新会话的成本很高,因此最好进行会话管理。我刚刚用一种单独的方法更新了这个问题,说明我是如何管理会话的。
  • @Jack Again from hibernate docs site - “如果 Session 抛出异常,事务必须回滚并丢弃会话。会话的内部状态可能与数据库不一致发生异常。”写代码时要注意这一点。
  • 我明白了,那我该如何创建 sessionFactory 对象呢?此外,在我当前的代码中,会话将设置为 threadlocal。我该怎么办?
  • 您的应用程序 conetext 或 beans xml 是否将 sessionFactory bean 定义为 spring 配置的 bean?如果是这样,那么您不必创建会话工厂。它可以注入你想要的地方。另外,我认为会话是 ThreadLocal 可以为您提供线程安全性。所以不会太在意。每个请求的会话是一种常见用法。 docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/… 所以,每次我去交易时,我都会从工厂得到一个会话。
  • 我建议这样做。 Spring 管理 bean 生命周期将摆脱很多头痛。
【解决方案3】:

如果您的应用程序是使用 java servlet 的 Web 应用程序,您可以添加一个 servlet 请求过滤器,您可以在其中启动会话甚至事务。然后可以使用同一个类来提交事务和刷新/关闭会话。 例如(没有错误处理):

import javax.servlet.*;

public class HibernateSessionRequestFilter implements Filter {

public void doFilter(ServletRequest request, ServletResponse response,
                  FilterChain chain) throws IOException, ServletException {

    Session session=HibernateUtil.getSession();
    session.beginTransaction();

    // Call the next filter (continue request processing)
    chain.doFilter(request, response);

    session.flush();
    session.getTransaction().commit();
}

【讨论】:

    【解决方案4】:

    通常会创建一个通用 dao 实现,并让所有其他 dao 扩展这个通用 dao。(请注意,这不是那么冗长,仅供参考):

    public interface GenericDAO<T, ID extends Serializable> {
        T save(T entity);
        void delete(T entity);
        }
    

    示例实现:

        public class GenericHibernateDAO<T, ID extends Serializable>
                implements GenericDAO<T, ID> {
            private Class<T> persistentClass;
    
            public GenericHibernateDAO() {
                this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                        .getGenericSuperclass()).getActualTypeArguments()[0];
            }
    
            private SessionFactory sessionFactory;
    
            public void setSessionFactory(SessionFactory sessionFactory) {
                this.sessionFactory = sessionFactory;
            }
    
             public Session getSession()
            {
                 return sessionFactory.getCurrentSession();
            }
    
            @Override
            public T save(T entity)
            {
                getSession().save(entity);
                return entity;
            }
            @Override
            public void delete(T entity) {
                getSession().delete(entity);        
            }
    }
    

    所以,例如,你创建你的道类似于:

    public class SampleHibernateDao extends
            GenericHibernateDAO<DomainObj, DomainObjId> implements SampleDAO {
    
    @Override
        public List<Object> findAnything(String find)
                throws HibernateException {
    
            Query query = getSession()
                    .createQuery(....)
    }
    
    }
    

    我想你会得到一个大概的想法。

    另外,使用 spring 来配置你的 sessionfactory,比如:

    <!-- DB settings -->
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/xxxx" />
            <property name="username" value="root" />
            <property name="password" value="root" />
            <property name="validationQuery" value="SELECT 1" />
            <property name="testOnBorrow" value="true" />
        </bean>
    
        <!-- Hibernate Settings -->
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    
            <property name="dataSource" ref="dataSource" />
            <property name="packagesToScan" value="com.xxx.xxx" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop>
                </props>
            </property>
            <property name="annotatedClasses">
                <list>
    
                    <value>com.xx.xxx.xxx.Domain</value>
                </list>
            </property>
    
        </bean>
    
        <tx:annotation-driven transaction-manager="hibernateTransactionManager" />
    
        <bean id="hibernateTransactionManager"
            class="org.springframework.orm.hibernate4.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    

    【讨论】:

      【解决方案5】:

      按照 M. Deinum 的评论和 tutorial,我设法删除了 HibernateUtil 并使用 Spring 来管理我的休眠会话。

      将以下内容添加到 project-servler.xml 文件以及添加所需的依赖项。

          <bean id="sessionFactory"
              class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <property name="configLocation" value="classpath:hibernate.cfg.xml" />
          </bean>
      
         <tx:annotation-driven />
      <bean id="transactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
          <property name="sessionFactory" ref="sessionFactory" />
      </bean>
      
      <bean id="userDao" class="net.codejava.spring.dao.UserDAOImpl">
      <constructor-arg>
          <ref bean="sessionFactory" />
      </constructor-arg>
      

      在我添加以上行之后,它遇到了提到的依赖注入错误here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-03
        • 2011-05-19
        • 1970-01-01
        • 2015-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多