在基于 Spring MVC 的应用程序中,我应该何时何地从 hibernate 4.3.8.Final 获取新会话?

When and where should I get a new session from hibernate 4.3.8.Final in a Spring MVC based Application?

我已经为我的 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 配置。

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

会话是一种 Hibernate 构造,用于调解与数据库的连接。 会话在创建时打开一个单一的数据库连接,并保持它直到会话关闭。 Hibernate从数据库加载的每一个对象都与session相关联,允许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 中打开休眠会话。

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

话虽如此,如果您在 spring 网络应用程序中使用容器管理的持久性,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();
 }

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

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();
}

通常会创建一个通用的 dao 实现,然后让所有其他 daos 扩展这个通用的 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);        
        }
}

所以,例如你创建你的 Dao 是这样的:

public class SampleHibernateDao extends
        GenericHibernateDAO<DomainObj, DomainObjId> implements SampleDAO {

@Override
    public List<Object> findAnything(String find)
            throws HibernateException {

        Query query = getSession()
                .createQuery(....)
}

}

我想你会有一个大概的想法。

此外,使用 spring 配置您的会话工厂,如:

<!-- 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>

我设法删除了 HibernateUtil 并使用 Spring 通过遵循 M. Deinum 的评论和此 tutorial.

来管理我的休眠会话

将以下内容添加到项目-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>

在我添加上面几行之后,它 运行 变成了提到的依赖注入错误