Spring 事务管理中的“无法为当前线程获取事务同步会话”错误

问题描述

我正在将应用程序从 Spring 3.2 迁移到 Spring 5。该应用程序使用 AWS RDS,主数据库作为主数据源或主数据源,副本数据库作为只读数据源。应用程序为主数据源创建一个会话工厂 (primarySessionFacotry) 实例,为只读数据源创建另一个 (readOnlySessionFactory) 实例,以便通过连接每个会话工厂,可以对两个数据源使用相同的 DAO。

appContext-hibernate.xml

<bean id="primaryDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass">
        <value>com.MysqL.jdbc.Driver</value>
    </property>
    <property name="jdbcUrl">
        <value>${database.jdbcurl}</value>
    </property>
    <property name="user">
        <value>${database.dbuser}</value>
    </property>
    <property name="password">
        <value>${database.dbpassword}</value>
    </property>

</bean>

<bean id="primarySessionFactory"
      class="org.springframework.orm.hibernate5.LocalSessionfactorybean">
    <property name="dataSource" ref="primaryDataSource"/>
    <property name="mappingResources" ref="hbmFileLocations">
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MysqLDialect</prop>
            <prop key="hibernate.show_sql">${hibernate.showsql}</prop>
            <prop key="hibernate.format_sql">${hibernate.formatsql}</prop>
        </props>
    </property>
</bean>

<util:list id="hbmFileLocations" value-type="java.lang.String">
    <value>com/xxxx/yyyyy/persistence/mappings/users.hbm.xml</value>
</util:list>

<bean id="primaryTransactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="primarySessionFactory">
    </property>
</bean>

<tx:annotation-driven transaction-manager="primaryTransactionManager"/>

appContext-hibernate-ro.xml

<bean id="readOnlyDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass">
        <value>com.MysqL.jdbc.Driver</value>
    </property>
    <property name="jdbcUrl">
        <value>${database.readonly.jdbcurl}</value>
    </property>
    <property name="user">
        <value>${database.readonly.dbuser}</value>
    </property>
    <property name="password">
        <value>${database.readonly.dbpassword}</value>
    </property>
    
</bean>

<bean id="readOnlySessionFactory"
      class="org.springframework.orm.hibernate5.LocalSessionfactorybean">
    <property name="dataSource" ref="readOnlyDataSource"/>
    <property name="mappingResources" ref="hbmFileLocations">
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MysqLDialect</prop>
            <prop key="hibernate.show_sql">${hibernate.readonly.showsql}</prop>
            <prop key="hibernate.format_sql">${hibernate.readonly.formatsql}</prop>
        </props>
    </property>

</bean>

<bean id="readOnlyTransactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="readOnlySessionFactory"/>
</bean>

<tx:annotation-driven transaction-manager="readOnlyTransactionManager"/>

DAO 是这样实例化的

<bean id="userDao" class="xxxxx.persistence.UserDao">
    <property name="sessionFactory" ref="primarySessionFactory"/>
</bean>
<bean id="readOnlyUserDao" class="xxxxx.persistence.UserDao">
    <property name="sessionFactory" ref="readOnlySessionFactory"/>
</bean>

这个 UserDao 有一个类似下面的方法

@Transactional
public List<User> getItems()
{
    return sessionFactory.getCurrentSession().createquery("from User user where isDeleted=false").list();
}

使用带有 readOnlyUserDao 实例的 UserDao.getItems() 方法时,我们遇到以下异常。

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:143)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:497)

请注意,相同的代码与 Spring( spring-orm 和 spring-tx 3.2)完美配合

请协助解决此问题。

解决方法

我遇到了同样的问题,并按照此处提到的步骤操作,这对我的情况很有效。 Spring Hibernate - Could not obtain transaction-synchronized Session for current thread

Session session;
try {
    session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
    session = sessionFactory.openSession();
}
return session;