【问题标题】:No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here没有 Hibernate Session 绑定到线程,并且配置不允许在此处创建非事务性会话
【发布时间】:2011-02-10 19:24:57
【问题描述】:

当我调用使用 SessionFactory.getCurrentSession() 的 DAO 方法时,我遇到了这个异常。 DAO 类使用@Transactional 进行注释,我还在应用程序上下文配置文件中声明了<tx:annotation-driven/>

我可以调用我的 DAO 方法来执行 HQL 查询,但是每当我调用一个首先获取 Hibernate 会话的 DAO 方法时,我就会遇到这个异常:

SEVERE: Failed to save the object.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
    at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.getCurrentSession(GenericDaoHibernateImpl.java:56)
    at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.saveOrUpdate(GenericDaoHibernateImpl.java:187)

我有以下应用上下文配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:flex="http://www.springframework.org/schema/flex"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/flex
                           http://www.springframework.org/schema/flex/spring-flex-1.0.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  load values used for bean properties  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>applicationContext.properties</value>
        </property>
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  DataSource where objects will be persisted  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="username" value="${datasource.username}" />
        <property name="password" value="${datasource.password}" />
        <property name="url" value="${datasource.url}" />
        <property name="driverClassName" value="${datasource.driver}" />
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Factory bean for Hibernate Sessions -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlDailyAvg</value>
                <value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlObservations</value>
                <value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlStation</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.use_sql_comments">true</prop>
                <prop key="hibernate.jdbc.batch_size">50</prop>
                <prop key="hibernate.query.substitutions">true 1, false 0</prop>
                <prop key="hibernate.max_fetch_depth">6</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddlauto}</prop>
                <prop key="hibernate.cache.use_second_level_cache">${hibernate.use_second_level_cache}</prop>
            </props>
        </property>
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  Transaction Manager bean  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="hibernateSessionFactory" />
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  enable the configuration of transactional behavior based on annotations  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <tx:annotation-driven transaction-manager="transactionManager" />


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  DAO for ESRL Station objects  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="esrlStationDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlStationDaoHibernateImpl">
        <property name="sessionFactory" ref="hibernateSessionFactory" />
        <property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlStation" />
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  DAO for ESRL Observations objects  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="esrlObservationsDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlObservationsDaoHibernateImpl">
        <property name="sessionFactory" ref="hibernateSessionFactory" />
        <property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlObservations" />
    </bean>


    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!--  DAO for ESRL daily average objects  -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="esrlDailyAvgDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlDailyAvgDaoHibernateImpl">
        <property name="sessionFactory" ref="hibernateSessionFactory" />
        <property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlDailyAvg" />
    </bean>


</beans> 

通用 DAO 类(我的程序中使用的 DAO 从中扩展)如下所示:

package gov.noaa.ncdc.cmb.persistence.dao;

import gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

/**
 * This class is an implementation of GenericDao<T, PK> using Hibernate.
 */
public class GenericDaoHibernateImpl<T extends PersistentEntity<PK>, PK extends Serializable>
    implements GenericDao<T, PK>
{
    private SessionFactory sessionFactory;

    static private Log log = LogFactory.getLog(GenericDaoHibernateImpl.class);

    private Class<T> persistentClass;

    /**
     * Can be used within subclasses as a convenience method.
     * 
     * @param criterionList the criteria to find by
     * @return the list of elements that match the specified criteria
     */
    protected List<T> findByCriteria(final List<Criterion> criterionList)
    {
        Criteria criteria = getCurrentSession().createCriteria(persistentClass);
        for (Criterion criterion : criterionList)
        {
            criteria.add(criterion);
        }
        return criteria.list();
    }

    protected String getCanonicalPersistentClassName()
    {
        return persistentClass.getCanonicalName();
    }

    /**
     * Gets the current Hibernate Session object.
     * 
     * @return
     */
    protected Session getCurrentSession()
    {
        return sessionFactory.getCurrentSession();
    }

    /*
     * This method only provided for interface compatibility.  Not recommended for use with large batches 
     * (this is an inefficient implementation, and it's somewhat difficult to perform batch operations with Hibernate).
     * 
     * (non-Javadoc)
     * @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#batchInsert(java.util.Collection)
     */
    @Override
    public int[] batchInsert(final Collection<T> entityCollection)
    {
        int[] updateCounts = new int[entityCollection.size()];
        int i = 0;
        for (T entity : entityCollection)
        {
            try
            {
                saveOrUpdate(entity);
                updateCounts[i] = 1;
                i++;
            }
            catch (Exception ex)
            {
                clear();
                throw new RuntimeException(ex);
            }
        }
        flush();
        clear();

        return updateCounts;
    }

    /*
     * This method only provided for interface compatibility.  Not recommended for use with large batches 
     * (this is an inefficient implementation, and it's somewhat difficult to perform batch operations with Hibernate).
     * 
     * (non-Javadoc)
     * @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#batchUpdate(java.util.Collection)
     */
    @Override
    public int[] batchUpdate(final Collection<T> entityCollection)
    {
        return batchInsert(entityCollection);
    }

    /**
     * Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do
     * not close open iterators or instances of ScrollableResults.
     */
    public void clear()
    {
        getCurrentSession().clear();
    }

    /*
     * (non-Javadoc)
     * @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#delete(gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity)
     */
    @Override
    public void delete(final T persistentObject)
    {
        getCurrentSession().delete(persistentObject);
    }

    /*
     * (non-Javadoc)
     * @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#findAll()
     */
    @Override
    public List<T> findAll()
    {
        return getCurrentSession().createQuery("from " + persistentClass.getName()).list();
    }

    /**
     * Finds a collection of entity objects which match to the example instance, minus any specified properties which should be excluded from the matching.
     * 
     * @param exampleInstance
     * @param excludeProperty
     * @return
     */
    public List<T> findByExample(final T exampleInstance,
                                 final String[] excludeProperty)
    {
        Criteria criteria = getCurrentSession().createCriteria(persistentClass);
        Example example = Example.create(exampleInstance);
        if (excludeProperty != null)
        {
            for (String exclude : excludeProperty)
            {
                example.excludeProperty(exclude);
            }
        }
        criteria.add(example);
        return criteria.list();
    }

    /*
     * (non-Javadoc)
     * @see com.sun.cloud.lifecycle.core.persistence.dao.GenericDao#findById(java.io.Serializable)
     */
    @Override
    public T findById(final PK id)
    {
        return (T) getCurrentSession().load(persistentClass, id);
    }

    /**
     * Force this session to flush. Must be called at the end of a unit of work, before commiting the transaction and
     * closing the session (depending on flush-mode, Transaction.commit() calls this method).
     * 
     * Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.
     */
    public void flush()
    {
        getCurrentSession().flush();
    }

    /*
     * (non-Javadoc)
     * @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#saveOrUpdate(gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity)
     */
    @Override
    public T saveOrUpdate(final T entity)
    {
        try
        {
            entity.setUpdatedDate(new Date());
            getCurrentSession().saveOrUpdate(entity);
            return entity;
        }
        catch (Exception ex)
        {
            String errorMessage = "Failed to save the object.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Setter for the persistentClass property.
     * 
     * @param persistentClass
     */
    public void setPersistentClass(final Class<T> persistentClass)
    {
        this.persistentClass = persistentClass;
    }

    /**
     * Property setter.
     * 
     * @param sessionFactory
     */
    public void setSessionFactory(final SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

}

我的应用程序从应用程序上下文中获取 DAO:

// load the Spring application context, get the DAOs
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] { "dailyAveragingApplicationContext.xml" });
esrlDailyAvgDao = (EsrlDailyAvgDao) applicationContext.getBean("esrlDailyAvgDao");
esrlObservationsDao = (EsrlObservationsDao) applicationContext.getBean("esrlObservationsDao");

当我尝试保存实体时遇到异常:

esrlDailyAvgDao.saveOrUpdate(esrlDailyAvg);

DAO 类本身使用 Transactional 注解:

@Transactional
public class EsrlDailyAvgDaoHibernateImpl
    extends GenericDaoHibernateImpl<EsrlDailyAvg, Long>
    implements EsrlDailyAvgDao

异常堆栈跟踪如下所示:

SEVERE: Failed to save the object.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
    at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.getCurrentSession(GenericDaoHibernateImpl.java:56)
    at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.saveOrUpdate(GenericDaoHibernateImpl.java:187)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
    at $Proxy19.saveOrUpdate(Unknown Source)
    at gov.noaa.ncdc.cmb.esrl.ingest.EsrlDailyAvgProcessor.main(EsrlDailyAvgProcessor.java:469)

【问题讨论】:

  • 您能否添加您正在使用的代码提取和配置示例。您是否配置了 SessionFactory bean?
  • DAO是不是被Spring注入到调用类中的?另外,您在堆栈跟踪中看到 TransactionInterceptor 了吗?
  • 所以其他几十个问题和谷歌搜索结果都没有帮助?
  • 用代码示例等编辑。配置了SessionFactory bean,并由Spring注入了DAO类。

标签: java hibernate spring


【解决方案1】:

我通过将 @Transactional 添加到基本/通用 Hibernate DAO 实现类(实现我在主程序中使用的 DAO 继承的 saveOrUpdate() 方法的父类)解决了这个问题,即 @Transactional 需要在实现该方法的实际类上指定。相反,我的假设是,如果我在子类上声明 @Transactional,那么它包含子类继承的所有方法。但是,@Transactional 注解似乎只适用于类中实现的方法,而不适用于类继承的方法。

【讨论】:

  • 刚刚发现这一点:static.springsource.org/spring/docs/2.0.x/reference/…“注释未被继承的事实意味着,如果您使用基于类的代理,则基于类的代理基础架构将无法识别事务设置,并且对象不会被包装在事务代理中(这肯定很糟糕)。所以请务必听取 Spring 团队的建议,只使用 @Transactional 注释来注释具体类(以及具体类的方法)。”
【解决方案2】:

我收到以下错误:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)

我通过更改我的休眠属性文件解决了这个问题

hibernate.current_session_context_class=thread

我的代码和配置文件如下

session =  getHibernateTemplate().getSessionFactory().getCurrentSession();

session.beginTransaction();

session.createQuery(Qry).executeUpdate();

session.getTransaction().commit();

关于属性文件

hibernate.dialect=org.hibernate.dialect.MySQLDialect

hibernate.show_sql=true

hibernate.query_factory_class=org.hibernate.hql.ast.ASTQueryTranslatorFactory

hibernate.current_session_context_class=thread

关于配置文件

<properties>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>         
<prop key="hibernate.query.factory_class">${hibernate.query_factory_class}</prop>       
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
</props>
</property>
</properties>

谢谢,
阿肖克

【讨论】:

  • 酷!非常感谢!我真的不想绑定到 Spring 事务注解。
【解决方案3】:

添加属性后:

&lt;prop key="hibernate.current_session_context_class"&gt;thread&lt;/prop&gt; 我得到如下异常:

org.hibernate.HibernateException: createQuery is not valid without active transaction
org.hibernate.HibernateException: save is not valid without active transaction.

所以我认为设置该属性不是一个好的解决方案。

最后我解决了“No Hibernate Session bound to thread”的问题:

1.&lt;!-- &lt;prop key="hibernate.current_session_context_class"&gt;thread&lt;/prop&gt; --&gt;
2.将&lt;tx:annotation-driven /&gt;添加到servlet-context.xml或dispatcher-servlet.xml
3.在@Service和@Repository之后添加@Transactional

【讨论】:

    【解决方案4】:

    我今天遇到了同样的问题。在这里搜索解决方案时,我犯了一个愚蠢的错误,那就是没有导入

    import org.springframework.transaction.annotation.Transactional;
    

    不知不觉我已经导入了

    import javax.transaction.Transactional;

    更改后一切正常。

    所以想到分享,如果有人犯同样的错误。

    【讨论】:

      【解决方案5】:

      您是否在 webapp 的 web.xml 中配置了 org.springframework.orm.hibernate3.support.OpenSessionInViewFilter(假设您的应用程序是 webapp),或者相应地包装调用?

      【讨论】:

      • 谢谢,但不,我没有在 Web 应用程序中使用此代码。
      【解决方案6】:

      每当您遇到以下错误时,请遵循它。

      org.hibernate.HibernateException: No Hibernate Session bound to thread,并且配置不允许在这里创建非事务性会话 在 org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)

      为实现类的每个方法打上@Transactional注解。

      【讨论】:

        【解决方案7】:

        我在共享函数中遇到了同样的错误,但它只发生在对该共享函数的某些调用中。我最终意识到调用共享函数的类之一没有将其包装在Unit of Work 中。一旦我使用工作单元更新了这些类函数,一切都按预期工作。

        因此,只需为遇到相同错误但已接受的答案不适用的任何未来访问者发布此内容。

        【讨论】:

          【解决方案8】:

          您的 spring 上下文中缺少 &lt;context:annotation-config /&gt;,因此不会扫描注释!

          【讨论】:

          • 感谢您的建议,不幸的是,我尝试将其添加到我的应用程序上下文配置中,但没有带来任何乐趣(顺便说一句,我以前从未使用过它来使注释生效)。
          • 你试过在调用DAO方法的方法中添加@Transactional吗?这可能看起来有点疯狂,但这可能是注释被设计为在控制器 -> 服务 -> DAO 层次结构中工作的结果,而您似乎正在跳过服务层。如果您的调用方法不在 Spring bean 中,您可以引入服务层或使用声明性事务管理而不是注释。
          • 再次感谢,rjsang。事实证明,该解决方案确实涉及将 @Transactional 添加到其他类,但它是将其添加到解决问题的基本 DAO 类(其中定义了 saveOrUpdate() 方法)。我错误地认为您可以将@Transactional 添加到一个类并让它对该类继承的所有方法生效,显然它不是那样工作的。因此,当我将 @Transactional 添加到我的子 DAO 类时,它只会影响该类中实际定义的方法,并且不会影响从基本/通用 DAO 类继承的方法。
          【解决方案9】:

          您可以在子类中使用@Transactional,但您必须覆盖每个方法并调用超级方法才能使其工作。

          示例:

          @Transactional(readOnly = true)
          public class Bob<SomeClass> {
             @Override
             public SomeClass getValue() {
                 return super.getValue();
             }
          }
          

          这允许它为它需要的每个方法进行设置。

          【讨论】:

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