Reputation: 445
I have a project which (temporarily) has two data sources wired up: a Neo4j database and a PostgreSQL database. The goal is to migrate from Neo4j to Postgres, but be able to switch back and forth during testing before making the switch permanent. Both databases are configured to use spring boot starter data. Neo4j works great, and has for a while, but I'm having difficulty getting transaction management to work with Postgres.
I am using Spring Boot 1.5.3, which I know is old, but upgrading it at this point is not an option.
The dependencies in the pom.xml file look like (along with driver dependencies, etc)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.8</version>
<scope>compile</scope>
</dependency>
I also have Flyway hooked up and running properly against the Postgres database.
Initially, I was using a DataSourceTransactionManager
for transaction management, but I was getting the following error when hitting a method annotated with @Transactional(transactionManager = "transactionManager")
(see configuration below for why I needed the transactionManager
property)
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress;
nested exception is javax.persistence.TransactionRequiredException: no transaction is in
progress
Most answers/tips about that particular error tell me that @Transactional
requires that (a) the annotated method is public (it is) and (b) it must be called from outside the bean (it is)--here's the relevant code:
@Component
public class MyEntityDao {
@Autowired
private MyEntityRepositoryHelper repositoryHelper;
public save(MyEntity entity) {
repositoryHelper.saveEntity(entity)
}
}
and
@Component
public class MyEntityRepositoryHelper {
@Autowired
private MyEntityRepository repository;
@Transactional(transactionManager = "transactionManager")
public MyEntity saveMyEntity(MyEntity entity) {
MyEntity saved = repository.saveAndFlush(entity);
... // do stuff with saved entity
saved = repository.saveAndFlush(entity);
... // do more stuff with saved entity
return saved;
}
...
}
and the repository:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
...
}
After much searching, I found that I needed a HibernateTransactionManager
, so I set about configuring one. Many hours and lots of research later, I have arrived at the following configuration (the data source is properly autoconfigured):
@Configuration
@EnableAsync
@EnableTransactionManagement
@PropertySources( {
@PropertySource(value = "classpath:application.properties",
ignoreResourceNotFound = false)
})
public class ApplicationConfig {
@Autowired
private Neo4jTransactionManager neo4jTransactionManager;
@Autowired
private HikariDataSource dataSource;
@Bean
public Neo4jTransactionManager neo4jTransactionManager() {
return neo4jTransactionManager;
}
@Bean
public SessionFactory postgresSessionFactory() {
return new LocalSessionFactoryBuilder(dataSource)
.addPackages("path.to.the.data.postgresql.model")
.buildSessionFactory();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new HibernateTransactionManager(postgresSessionFactory());
}
...
}
Now I'm getting this error:
org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate
Session for transaction; nested exception is java.lang.NoClassDefFoundError: org/hibernate
/engine/transaction/spi/TransactionContext
I have spent hours already on this and I really need to start moving forward. Any ideas?
Edit: I realized that Hibernate 5 is being used and I had some imports from Hibernate 4 in the ApplicationConfig class. I switched those to Hibernate 5 and I'm back to the "no transaction is in progress" error.
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction
is in progress; nested exception is
javax.persistence.TransactionRequiredException: no transaction is in
progress
Upvotes: 2
Views: 4308
Reputation: 445
It appears that I was misinformed when told that I needed a HibernateTransactionManager
. What I really needed was a JpaTransactionManager
. I changed ApplicationConfig to the following and it worked.
@Configuration
@EnableAsync
@EnableTransactionManagement
@PropertySources( {
@PropertySource(value = "classpath:application.properties",
ignoreResourceNotFound = false)
})
public class ApplicationConfig {
@Autowired
private Neo4jTransactionManager neo4jTransactionManager;
@Bean
public Neo4jTransactionManager neo4jTransactionManager() {
return neo4jTransactionManager;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
...
}
Upvotes: 2