简体   繁体   中英

Spring 4.3 -> 5.3 and Hibernate 3.6 -> 5.4 upgrade results in error: Could not obtain transaction-synchronized Session for current thread

I am currently upgrading a project from Hibernate 3 to Hibernate 5 and from Java 7 to Java 17. We also use Spring 4.3.x which I updated to the 5.3 stream. After updating Spring, I get the error stated in the headline. Here's my code:

I have my own implementation of the sessionFactory with this import

import org.springframework.orm.hibernate3.LocalSessionFactoryBuilder;

that i changed to

import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;

Here's my SessionFactory

public class MySessionFactory extends LocalSessionFactoryBean {
    
    /** The Constant defaultLocationPattern. */
    private final static String defaultLocationPattern = "classpath*:/com/*/*/**/integration/hbm/*.hbm.xml";
    
    /** The Constant log. */
    private static final Log log = LogFactory.getLog(MySessionFactory.class);
    
    /**  The location pattern. */
    private String locationPattern;



//--------------------------------------------------------------------------------------------------------------------------------------------------------------    
    
    /**
     * Collect hbm resources.
     */
    private void collectHbmResources() {
        try {
            final ClassLoader cl = this.getClass().getClassLoader(); 
            final ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
            final Resource[] items = resolver.getResources(locationPattern != null ? locationPattern:defaultLocationPattern);
            
            log.info("Found " + items.length + " resources");
            
            for (Resource resource:items) {
                log.info("........................................................" + resource.getFilename());
            }
            
            setMappingLocations(items);
        } catch (Exception e) {
            log.error(e);
        }
    }
    
//--------------------------------------------------------------------------------------------------------------------------------------------------------------    

    @Override
    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {

        collectHbmResources();
        return super.buildSessionFactory(sfb);
    }

//--------------------------------------------------------------------------------------------------------------------------------------------------------------    
    
    /**
     * Sets the location pattern.
     *
     * @param locationPattern the new location pattern
     */
    public void setLocationPattern(final String locationPattern) {
        this.locationPattern = locationPattern;
    }
}

For the update of Spring 4.x to 5.x and Hibernate 3.x to 5.x and when I changed the import from v3 to v5, I had to add the parameter sfb to the buildSessionFactory method because the signature of this method had changed. After that, I started to get the error from the headline.

The error is thrown in Spring's SpringSessionContext#currentSession() method. None of the given if-clauses turn out to be true which is why at the end of the method, this exception is thrown.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'txtTextItemsCacheService' defined in class path resource [com/cl/myApp/core/txt/service/spring-txt-service-beans.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at deployment.myApp.war//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at deployment.myApp.war//org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
        ... 80 more
Caused by: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
        at deployment.myApp.war//org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:142)
        at deployment.myApp.war//org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:491)
        at deployment.myApp.war//com.pal.myApp.common.integration.dao.impl.CommonHibernateDAOImpl.findByCriteria(CommonHibernateDAOImpl.java:521)
        at deployment.myApp.war//com.pal.myApp.common.service.impl.CommonCrudServiceImpl.findByCriteria(CommonCrudServiceImpl.java:46)
        at deployment.myApp.war//com.pal.myApp.common.service.impl.CommonCrudServiceImpl.findByCriteria(CommonCrudServiceImpl.java:30)
        at deployment.myApp.war//com.cl.myApp.core.pm.service.impl.PmPropertyServiceImpl.findByKey(PmPropertyServiceImpl.java:67)
        at deployment.myApp.war//com.cl.myApp.core.pm.service.impl.PmPropertyServiceImpl.findValueByKey(PmPropertyServiceImpl.java:109)
        at deployment.myApp.war//com.cl.myApp.core.pm.service.impl.PmPropertyValueServiceImpl.getPropertyValue(PmPropertyValueServiceImpl.java:105)
        at deployment.myApp.war//com.cl.myApp.core.pm.service.impl.PmPropertyValueServiceImpl.getStringProperty(PmPropertyValueServiceImpl.java:26)
        at deployment.myApp.war//com.cl.myApp.core.txt.service.TxtTextItemsCacheServiceImpl.init(TxtTextItemsCacheServiceImpl.java:52)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1930)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1872)
        at deployment.myApp.war//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
        ... 87 more

Here's the sessionFactoryBean. As you can see, I also tried to uncomment the custom sessionFactory and use the out of the box solution instead, but that results in the same error.

<!--  Hibernate SessionFactory Definition -->
<!--    <bean id="sessionFactory" class="com.myApp.common.integration.orm.MySessionFactory">-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="mappingLocations" value="classpath*:/com/*/*/**/integration/hbm/*.hbm.xml" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.cglib.use_reflection_optimizer">${hibernate.cglib.use_reflection_optimizer}</prop>
                <prop key="hibernate.cache.provider_class">${hibernate.cache.provider_class}</prop>
                <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
                <prop key="hibernate.connection.release_mode">${hibernate.connection.release_mode}</prop>               
                <prop key="hibernate.c3p0.min_size">${hibernate.c3p0.min_size}</prop>
                <prop key="hibernate.c3p0.max_size">${hibernate.c3p0.max_size}</prop>
                <prop key="hibernate.c3p0.timeout">${hibernate.c3p0.timeout}</prop>
                <prop key="hibernate.c3p0.max_statements">${hibernate.c3p0.max_statements}</prop>
                <prop key="hibernate.c3p0.idle_test_period">${hibernate.c3p0.idle_test_period}</prop>
            </props>
        </property>
        <property name="dataSource" ref="dataSource"/>
    </bean>

And to dig a little deeper: I have an extension class CommonDaoHibernateImpl.java which is an extension to every DAO i use. In this class, I have methods like this:

@Override
    @SuppressWarnings({ "unchecked", "deprecation" })
    public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {
        
        final Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(entityClass);
        final Set<String> keys = criteriaMap.keySet();

... }

The error occurs in the first line, so I tried to change it like that:

    public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {
            Session session;
            try {
                session = this.getSessionFactory().getCurrentSession();
} catch (HibernateException e) {
   session = this.getSessionFactory().openSession();
}

... }

Which gives me a different error, which is a good thing but still an error:

Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:/MyConnection
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:159)
        at org.jboss.as.connector@23.0.2.Final//org.jboss.as.connector.subsystems.datasources.WildFlyDataSource.getConnection(WildFlyDataSource.java:64)
        at deployment.myApp.war//org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
        at deployment.myApp.war//org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38)
        at deployment.myApp.war//org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:108)
        ... 103 more
Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:/MyConnection
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:690)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.getManagedConnection(TxConnectionManagerImpl.java:440)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:789)
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:151)
        ... 107 more
Caused by: javax.resource.ResourceException: IJ031084: Unable to create connection
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createLocalManagedConnection(LocalManagedConnectionFactory.java:345)
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.getLocalManagedConnection(LocalManagedConnectionFactory.java:352)
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createManagedConnection(LocalManagedConnectionFactory.java:287)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.createConnectionEventListener(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:1322)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.getConnection(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:499)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:632)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:604)
        at org.jboss.ironjacamar.impl@1.4.27.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:624)
        ... 110 more
Caused by: org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
        at org.postgresql@42.2.20//org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:613)
        at org.postgresql@42.2.20//org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:161)
        at org.postgresql@42.2.20//org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213)
        at org.postgresql@42.2.20//org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51)
        at org.postgresql@42.2.20//org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:223)
        at org.postgresql@42.2.20//org.postgresql.Driver.makeConnection(Driver.java:465)
        at org.postgresql@42.2.20//org.postgresql.Driver.connect(Driver.java:264)
        at org.jboss.ironjacamar.jdbcadapters@1.4.27.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createLocalManagedConnection(LocalManagedConnectionFactory.java:321)
        ... 117 more

It indicates that the system is creating a lot of sessions (?) during startup of the app until the threshold is reached.

I think I have found a solution for my problem. I have an abstract class CommonHibernateDAOImpl.java which is extended by every DAO that I have and implements some methods used by each DAO, such as

@Override
@SuppressWarnings({ "unchecked", "deprecation" })
public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {

    final Criteria criteria = sessionFactory.getCurrentSession().createCriteria(entityClass);
    return criteria.list();
}

While this method the way it is would run perfectly fine in Hibernate 3.6, it stopped working in 5.4 and results in the error:

Caused by: org.hibernate.HibernateException: Calling method 'createCriteria' is not valid without an active transaction (Current status: NOT_ACTIVE)

To solve this, I DI'ed the sessionFactory with @Autowire and rewrote the way the sessions are being created and I closed the session, which according to the docs is not necessary when using .getCurrentSessioon() instead of .openSession() . But just to make sure that this is not causing the problems with the "too many connections" anymore.

@Autowire
private SessionFactory sessionFactory;

@Override
public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {

    Session session = sessionFactory.getCurrentSession();
    final Criteria criteria;
    List ret = null;

    try {
        Transaction tx = session.beginTransaction();
        criteria = session.createCriteria(entityClass);
        ret = criteria.list();
    } catch (HibernateException e) {

    } finally {
        if (currentSession() != null) {
            currentSession().close();
        }
    }
    return ret;
}

And for whatever weird reason, it worked as soon as I manually created a transaction before, which was not done with Hibernate 3.6. And I am absolutely sure that this is not the way it should because I have code, that uses the Spring-internal getHibernateTemplate().findAll() function:

@Override
public final List<ENTITY> findAll(final Class<ENTITY> entityClass) {
    return getHibernateTemplate().loadAll(entityClass);
}

Which does not create a new transaction for me and there, the same error appears:

@Override
    @SuppressWarnings({"unchecked", "deprecation"})
    public <T> List<T> loadAll(Class<T> entityClass) throws DataAccessException {
        return nonNull(executeWithNativeSession((HibernateCallback<List<T>>) session -> {
            Criteria criteria = session.createCriteria(entityClass);
            criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
            prepareCriteria(criteria);
            return criteria.list();
        }));
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM