I have two databases and i’m trying to save some records to both of them inside a service method.
This gives me the error: org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
.
Here is entities:
@Entity @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Table(name = "TABLE_NAME") public class SomeEntity { @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator") @SequenceGenerator(name = "seq_generator", sequenceName = "SEQ_ID", allocationSize = 1) @Id @Column(name = "ID", nullable = false) private Long id; @Column(name = "SOME_STR", nullable = false) private String someStr; @Column(name = "SOME_INT", nullable = false) private Integer someInt; public SomeEntity(String someStr, Integer someInt) { this.someStr = someStr; this.someInt = someInt; } } @Entity @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Table(name = "TABLE_NAME") public class SomeEntityHist { @Id @Column(name = "ID", nullable = false) private Long id; @Column(name = "SOME_STR", nullable = false) private String someStr; @Column(name = "SOME_INT", nullable = false) private Integer someInt; }
And here is one of the config files for multiple db connection:
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "realEntityManager", basePackages = {"com.some.project.files.repository.real"} ) @RequiredArgsConstructor @Log4j2 @AutoConfigureOrder(1) public class RealDatasourceConfig { private final Environment env; @Primary @Bean public DataSource realDataSource() throws SQLException { HikariDataSource hikariDataSource = new HikariDataSource(); hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("real.driver-class-name"), "oracle.jdbc.OracleDriver")); hikariDataSource.setJdbcUrl(env.getProperty("real.db-url")); hikariDataSource.setUsername(env.getProperty("real.username")); hikariDataSource.setPassword(env.getProperty("real.password")); hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("real.minPoolSize"), "1"))); hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("real.maxPoolSize"), "10"))); Properties props = new Properties(); props.setProperty("maxStatements", env.getProperty("real.maxStatements", "300")); hikariDataSource.setDataSourceProperties(props); hikariDataSource.setPoolName(env.getProperty("real.pool-name")); hikariDataSource.setConnectionTestQuery(env.getProperty("real.connection-test-query")); return hikariDataSource; } @Primary @Bean public LocalContainerEntityManagerFactoryBean realEntityManager(EntityManagerFactoryBuilder builder) throws SQLException { return builder .dataSource(realDataSource()) .packages("com.some.project.files.entity.real") .persistenceUnit("real") .build(); } @Primary @Bean(name = "transactionManager") public JpaTransactionManager realTransactionManager(EntityManagerFactory realEntityManager) { return new JpaTransactionManager(realEntityManager); } }
And here is the other one:
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "histEntityManager", basePackages = {"com.some.project.files.repository.hist"} ) @RequiredArgsConstructor @Log4j2 @AutoConfigureOrder(3) public class HistDatasourceConfig { private final Environment env; @Bean public DataSource histDataSource() throws SQLException { HikariDataSource hikariDataSource = new HikariDataSource(); hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("hist.driver-class-name"), "oracle.jdbc.OracleDriver")); hikariDataSource.setJdbcUrl(env.getProperty("hist.jdbc-url")); hikariDataSource.setUsername(env.getProperty("hist.username")); hikariDataSource.setPassword(env.getProperty("hist.password")); hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.minPoolSize"), "1"))); hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.maxPoolSize"), "10"))); Properties props = new Properties(); props.setProperty("maxStatements", env.getProperty("hist.maxStatements", "300")); hikariDataSource.setDataSourceProperties(props); hikariDataSource.setPoolName(env.getProperty("hist.pool-name")); hikariDataSource.setConnectionTestQuery(env.getProperty("hist.connection-test-query")); return hikariDataSource; } @Bean("histEntityManager") public LocalContainerEntityManagerFactoryBean histEntityManager(EntityManagerFactoryBuilder builder) throws SQLException { return builder .dataSource(histDataSource()) .packages("com.some.project.files.entity.hist") .persistenceUnit("hist") .build(); } @Bean public JpaTransactionManager histTransactionManager(EntityManagerFactory histEntityManager) { return new JpaTransactionManager(histEntityManager); } }
The problem is about the hist entity. If i save just the other one it saves.
But if i try to save the hist entity like this:
@Override @Transactional public void someMethod() { SomeEntity entity = new SomeEntity("abc", 123); SomeRepository.save(entity); SomeEntityHist entityHist = new SomeEntityHist(1L, "abc", 123); SomeRepositoryHist.save(entityHist); }
it saves the first one but it doesn’t save the hist and when i look at the logs it just calls a select query and not insert.
And if i try to save the hist entity with saveAndFlush
method it gives the error.
What is the reason what can i do about it. Is it about config files?
Advertisement
Answer
Both answers posted by @Airy and @GJohannes is pointing out the parts i was missing but there is also one thing i needed to add:
@Qualifier("histEntityManager")
Here is the final config file that works for me:
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "histEntityManager", transactionManagerRef = "histTransactionManager", basePackages = {"com.some.project.files.repository.hist"} ) @RequiredArgsConstructor @Log4j2 @AutoConfigureOrder(3) public class HistDatasourceConfig { private final Environment env; @Bean public DataSource histDataSource() throws SQLException { HikariDataSource hikariDataSource = new HikariDataSource(); hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("hist.driver-class-name"), "oracle.jdbc.OracleDriver")); hikariDataSource.setJdbcUrl(env.getProperty("hist.jdbc-url")); hikariDataSource.setUsername(env.getProperty("hist.username")); hikariDataSource.setPassword(env.getProperty("hist.password")); hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.minPoolSize"), "1"))); hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.maxPoolSize"), "10"))); Properties props = new Properties(); props.setProperty("maxStatements", env.getProperty("hist.maxStatements", "300")); hikariDataSource.setDataSourceProperties(props); hikariDataSource.setPoolName(env.getProperty("hist.pool-name")); hikariDataSource.setConnectionTestQuery(env.getProperty("hist.connection-test-query")); return hikariDataSource; } @Bean("histEntityManager") public LocalContainerEntityManagerFactoryBean histEntityManager(EntityManagerFactoryBuilder builder) throws SQLException { return builder .dataSource(histDataSource()) .packages("com.some.project.files.entity.hist") .persistenceUnit("hist") .build(); } @Bean public JpaTransactionManager histTransactionManager(@Qualifier("histEntityManager") EntityManagerFactory histEntityManager) { return new JpaTransactionManager(histEntityManager); } }
And ofcourse i’m adding @Transactional(transactionManager = "histTransactionManager")