I was setting up a simple side-project with Spring, Spring Data and Hibernate. My repositories are generated using Spring Data’s jpa:repositories to save on development time and the database schema is generated using Hibernate, based on my JPA and JSR-303 annotated entities.
However, one of the issues I encountered was that my repositories did not insert anything in the database. After searching the web I followed a suggestion on Stackoverflow to add an entityManager.flush() to the repository to see if a transaction was running or not. There was indeed no transaction running but I had yet to find out why.
Exception in thread "main" javax.persistence.TransactionRequiredException: no transaction is in progress at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1136) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1297) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:344) at com.sun.proxy.$Proxy31.flush(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293) at com.sun.proxy.$Proxy31.flush(Unknown Source) at be.pw999.photocol.service.PhotoColServiceImpl.makeImage(PhotoColServiceImpl.java:45) ...
At that point my code looked like this:
@Service
public class PhotoColServiceImpl implements PhotoColService {
// My Injected beans
@Autowired
private EntityManager em;
@Transactional(readOnly = false)
@Override
public MakeImageResponse makeImage(MakeImageRequest request) {
em.flush();
}
}
public class Backend {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/META-INF/webapp/WEB-INF/application-context.xml");
PhotoColService service = ac.getBean(PhotoColService.class);
MakeImageRequest r = new MakeImageRequest("DSC0012.JPG", "/home/phillip/Pictures", "12345678901234567890123456789012");
MakeImageResponse resp = service.makeImage(r);
ac.close();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd">
<context:component-scan base-package="be.pw999.photocol.service"></context:component-scan>
<!-- Define your application beans here. They will be available to the
beans defined in your web-context because it is a sub-context. Beans defined
in the web-context will not be available in the application context. -->
<context:property-placeholder location="classpath:persistence-h2.properties" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="be.pw999.photocol.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.pass}" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="database" value="H2" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<!-- spring based scanning for entity classes> -->
<property name="packagesToScan" value="be.pw999.photocol.model" />
</bean>
<jpa:repositories base-package="be.pw999.photocol.repository"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>
There were actually a couple of issues with this configuration.
Mistake 1
Using the wrong @Transaction annotation. There are two @Transaction annotations: @javax.persistence.Transaction and @org.springframework.transaction.annotation.Transactional. Only the latter will add transaction management to your Spring services and I had used the first one (because I trust autocomplete too much).
Mistake 2
Second mistake was that I had forgotten to add <tx:annotation-driven /> to my configuration. Stupid, stupid, stupid !
Mistake 3
After solving the first two mistakes I still had no transaction and no inserts in my H2 database. I quickly realized that my transaction manager was an instance of org.springframework.orm.hibernate5.HibernateTransactionManager and even though I use Hibernate, I’m not using it directly. My repositories are generated with Spring’s jpa:repositories and they use a JPA EntityManager instead of the Hibernate SessionFactory. So even though my configuration looked great, the JPA repositories did not use the Hibernate managed transactions.
So instead of using the HibernateTransactionManager, I used the org.springframework.orm.jpa.JpaTransactionManager and this did solve the no-inserts problem.
So this is now my configuration that works:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<context:component-scan base-package="be.pw999.photocol.service"></context:component-scan>
<!-- Define your application beans here. They will be available to the
beans defined in your web-context because it is a sub-context. Beans defined
in the web-context will not be available in the application context. -->
<context:property-placeholder location="classpath:persistence-h2.properties" />
<tx:annotation-driven transaction-manager="jpaTransactionManager" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="be.pw999.photocol.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.pass}" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="database" value="H2" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<!-- spring based scanning for entity classes> -->
<property name="packagesToScan" value="be.pw999.photocol.model" />
</bean>
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<jpa:repositories base-package="be.pw999.photocol.repository"
transaction-manager-ref="jpaTransactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>


