Home > other >  Spring 4.3 -> 5.3 and Hibernate 3.6 -> 5.4 upgrade results in error: Could not obtain transact
Spring 4.3 -> 5.3 and Hibernate 3.6 -> 5.4 upgrade results in error: Could not obtain transact

Time:08-31

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" >-->
    <bean id="sessionFactory" >
        <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 [email protected]//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:159)
        at [email protected]//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 [email protected]//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:690)
        at [email protected]//org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.getManagedConnection(TxConnectionManagerImpl.java:440)
        at [email protected]//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:789)
        at [email protected]//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:151)
        ... 107 more
Caused by: javax.resource.ResourceException: IJ031084: Unable to create connection
        at [email protected]//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createLocalManagedConnection(LocalManagedConnectionFactory.java:345)
        at [email protected]//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.getLocalManagedConnection(LocalManagedConnectionFactory.java:352)
        at [email protected]//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createManagedConnection(LocalManagedConnectionFactory.java:287)
        at [email protected]//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.createConnectionEventListener(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:1322)
        at [email protected]//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.getConnection(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:499)
        at [email protected]//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:632)
        at [email protected]//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:604)
        at [email protected]//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 [email protected]//org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:613)
        at [email protected]//org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:161)
        at [email protected]//org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213)
        at [email protected]//org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51)
        at [email protected]//org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:223)
        at [email protected]//org.postgresql.Driver.makeConnection(Driver.java:465)
        at [email protected]//org.postgresql.Driver.connect(Driver.java:264)
        at [email protected]//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.

CodePudding user response:

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