Hibernate & Spring: Exception when trying to create a transaction

user2223059 picture user2223059 · Oct 2, 2015 · Viewed 15.3k times · Source

So I'm trying to use Spring to manage hibernate transactions for the first time, and something's going wrong. I'm not sure what. I've looked at a bunch of similar answers on this site and nothing I've seen seems to be right.

So, I'm gonna copy and paste a bunch of my code with some explanations and ask for help here.

Here is a stack trace of the exception I'm getting. Essentially, It seems that it's trying to find org.hibernate.engine.transaction.spi.transactioncontext, and can't.

Exception stack trace

EXCEPTION: Could not open Hibernate Session for transaction; nested exception is java.lang.NoClassDefFoundError: org/hibernate/engine/transaction/spi/TransactionContext
org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:544)
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:427)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:276)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
com.sun.proxy.$Proxy42.getSavedPortfolios(Unknown Source)
io.craigmiller160.stockmarket.controller.StockMarketController.showOpenPortfolioDialog(StockMarketController.java:994)
io.craigmiller160.stockmarket.controller.StockMarketController.parseEvent(StockMarketController.java:431)
io.craigmiller160.stockmarket.controller.StockMarketController.processEvent(StockMarketController.java:336)
io.craigmiller160.mvp.concurrent.AbstractConcurrentListenerController$1.run(AbstractConcurrentListenerController.java:209)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)

Now, I've searched this site, and the big thing I saw was that this means I have a dependency wrong in my pom.xml. The thing is, I have the most up-to-date version of the hibernate-core dependency in my pom. From what I've read, that's what I need for this class.

pom.xml dependencies

<dependencies>
<!-- JUnit Testing -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
<!-- MVP Framework -->
    <dependency>
        <groupId>io.craigmiller160.mvp</groupId>
        <artifactId>mvp-framework</artifactId>
        <version>2.1.1</version>
    </dependency>
<!-- MigLayout -->
    <dependency>
        <groupId>com.miglayout</groupId>
        <artifactId>miglayout-swing</artifactId>
        <version>5.0</version>
    </dependency>
<!-- JFreeChart -->
    <dependency>
        <groupId>org.jfree</groupId>
        <artifactId>jfreechart</artifactId>
        <version>1.0.19</version>
    </dependency>
<!-- Java Concurrency In Practice Annotations -->
    <dependency>
        <groupId>net.jcip</groupId>
        <artifactId>jcip-annotations</artifactId>
        <version>1.0</version>
    </dependency>
<!-- Joda Time -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.8.2</version>
    </dependency>
<!-- MySQL ConnectorJ -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.36</version>
    </dependency>
<!-- Spring Framework Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
<!-- Spring Framework Beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
<!-- Spring Framework Context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
<!-- Hibernate Core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.0.1.Final</version>
    </dependency>
<!-- XML Framework -->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
<!-- Code Generation library -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
<!-- Apache Commons Logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
<!-- LOG4J API -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>${log4j.version}</version>
    </dependency>
<!-- LOG4J Core -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>${log4j.version}</version>
    </dependency>
<!-- SLF4J/LOG4J Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>${log4j.version}</version>
    </dependency>
<!-- LOG4J/Commons Logging Binding -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-jcl</artifactId>
        <version>${log4j.version}</version>
    </dependency>
<!-- SLF4J API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.12</version>
    </dependency>
<!-- Spring ORM -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
<!-- AspectJ Runtime -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
<!-- AspectJ Weaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
<!-- Apache Database Connection Pooling -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

Also, I'm adding the actual method in my DAO that I'm calling. This method is what is attempting to run when the exception is thrown.

DAO method:

@Transactional
@Override
@SuppressWarnings("unchecked") //hibernate list() method doesn't support generics
public List<String> getSavedPortfolios() throws HibernateException {
    List<String> portfolioNames = new ArrayList<>();

    List<SQLPortfolioModel> portfolioList = sessionFactory.getCurrentSession()
                                .createCriteria(PortfolioModel.class)
                                .list();

    for(SQLPortfolioModel portfolio : portfolioList){
        int id = portfolio.getUserID();
        String name = portfolio.getPortfolioName();
        BigDecimal netWorth = portfolio.getNetWorth();
        Calendar timestamp = portfolio.getTimestamp();

        String fileName = String.format("%1$d-%2$s-%3$s-"
                +"%4$s", id, name, moneyFormat.format(netWorth), 
                timestampFormat.format(timestamp.getTime()));
        portfolioNames.add(fileName);
    }

    return portfolioNames;
}

Lastly, here is my spring-context-data.xml. It contains all the configuration for my data beans for spring, plus the transaction stuff:

spring-context-data.xml

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

<!-- Sets annotation-driven transactions -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- DataSource object for providing database connections -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost/stockmarket"/>
    <property name="username" value="stockmarket"/>
    <property name="password" value="stockmarket"/>
</bean>

<!-- SessionFactory object for creating sessions for database access -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- <property name="configLocation" value="classpath:hibernate.cfg.xml"/>-->
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="connection.pool_size">1</prop>
            <prop key="show_sql">false</prop>
            <!-- Might need this one below for transactions, not sure yet -->
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
        </props>
    </property>
    <property name="annotatedClasses">
        <list>
            <value>io.craigmiller160.stockmarket.stock.AbstractStock</value>
            <value>io.craigmiller160.stockmarket.stock.OwnedStock</value>
            <value>io.craigmiller160.stockmarket.stock.DefaultStock</value>
            <value>io.craigmiller160.stockmarket.stock.DefaultOwnedStock</value>
            <value>io.craigmiller160.stockmarket.model.PortfolioModel</value>
            <value>io.craigmiller160.stockmarket.model.SQLPortfolioModel</value>
        </list>
    </property>
</bean>

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

<!-- HibernateDAO class for performing database operations -->
<bean id="hibernateDao" class="io.craigmiller160.stockmarket.controller.HibernatePortfolioDAO"
    destroy-method="closeFactory">
    <constructor-arg ref="sessionFactory"/>
</bean>

 </beans>

So I just have no idea why this is happening. I've double and triple checked what I did versus what I'm seeing online, and I can't see the mistake. This is my first time trying to use Spring transaction management. Any help would be greatly appreciated.

PS. I'm using Spring 4 & Hibernate 5 together, if that makes a difference.

Answer

Tom Chamberlain picture Tom Chamberlain · Oct 29, 2015

In your POM you are depending on Hibernate 5, but in your transaction manager, you are using Hibernate 4.

Change your transacation manager to match your pom (i.e. from hibernate4 to hibernate5):

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

If that causes a class not found error, upgrade your spring framework to 4.2.2