Issue
I have transactional service that I use to create a Person and persist it in db. The problem is that entity is not saved into db after transaction is commited.
At first I thought it was the fault of the JpaRepository, which opens a new transaction every time a method is called, but from what I've learned, these are just logical transactions inside a single physical one that gets opened in my service.
I use Spring Boot and Hibernate to connect with my firebird databases. For this purpose i configured two separate datasources and transaction managers. In each @Transactional annotation, I specify the transaction manager to be used. Fetching data works perfect. The problem concerns only saving.
I'm actually performing more operations here, but for the sake of the example I'll limit myself to just saving the entities. Simple example below.
@Transactional("secondaryTransactionManager")
@RequiredArgsConstructor
public class PersonServiceImpl implements PersonService {
private final PersonJpaRepository personJpaRepository;
@Override
public String savePerson() {
var personEntity = new PersonEntity();
personEntity.setName("Slawek");
personEntity.setSurname("Filip");
personEntity.setPersonalNumber("12341234123");
var save = personJpaRepository.save(personEntity);
return save.getName();
}
}
Person entity is not saved in db though. There are logs
2021-10-16 17:41:13.345 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : Getting transaction for [com.backend.declarations.service.impl.PersonServiceImpl.savePerson]
2021-10-16 17:41:13.772 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:13.967 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:14.716 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2021-10-16 17:41:14.728 DEBUG 884 --- [nio-8080-exec-3] o.s.orm.jpa.EntityManagerFactoryUtils : Opening JPA EntityManager
2021-10-16 17:41:14.728 TRACE 884 --- [nio-8080-exec-3] .i.SessionFactoryImpl$SessionBuilderImpl : Opening Hibernate Session. tenant=null
2021-10-16 17:41:14.729 TRACE 884 --- [nio-8080-exec-3] org.hibernate.internal.SessionImpl : Opened Session [16b4f365-b4f9-4267-bed6-a5057aad25a9] at timestamp: 1634398874728
2021-10-16 17:41:14.735 TRACE 884 --- [nio-8080-exec-3] o.hibernate.event.internal.EntityState : Transient instance of: com.backend.declarations.repository.entity.PersonEntity
2021-10-16 17:41:14.736 TRACE 884 --- [nio-8080-exec-3] o.h.e.i.DefaultPersistEventListener : Saving transient instance
2021-10-16 17:41:14.738 DEBUG 884 --- [nio-8080-exec-3] org.hibernate.SQL : select gen_id( GEN_OSOBY_ID, 1 ) from RDB$DATABASE
2021-10-16 17:41:14.750 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Registering statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.752 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Registering result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.760 DEBUG 884 --- [nio-8080-exec-3] o.h.id.enhanced.SequenceStructure : Sequence value obtained: 3422
2021-10-16 17:41:14.760 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.760 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Closing result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.764 DEBUG 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : HHH000387: ResultSet's statement was not registered
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Closing prepared statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.e.jdbc.internal.JdbcCoordinatorImpl : Starting after statement execution processing [ON_CLOSE]
2021-10-16 17:41:14.765 DEBUG 884 --- [nio-8080-exec-3] o.h.e.i.AbstractSaveEventListener : Generated identifier: 3422, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
2021-10-16 17:41:14.765 TRACE 884 --- [nio-8080-exec-3] o.h.e.i.AbstractSaveEventListener : Saving [com.backend.declarations.repository.entity.PersonEntity#3422]
2021-10-16 17:41:14.769 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue : Adding an EntityInsertAction for [com.backend.declarations.repository.entity.PersonEntity] object
2021-10-16 17:41:14.772 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue : Adding insert with no non-nullable, transient entities: [EntityInsertAction[com.backend.declarations.repository.entity.PersonEntity#3422]]
2021-10-16 17:41:14.772 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue : Adding resolved non-early insert action.
2021-10-16 17:41:14.778 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2021-10-16 17:41:14.871 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:14.951 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:18.392 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor : Completing transaction for [com.backend.declarations.service.impl.PersonServiceImpl.savePerson]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] org.hibernate.internal.SessionImpl : Closing session [16b4f365-b4f9-4267-bed6-a5057aad25a9]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.e.jdbc.internal.JdbcCoordinatorImpl : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@695993a3]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.LogicalConnectionManagedImpl : Closing logical connection
2021-10-16 17:41:18.394 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources
2021-10-16 17:41:18.395 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.LogicalConnectionManagedImpl : Logical connection closed
Primary Ts configuration:
@Configuration
@EnableJpaRepositories(basePackages = {
"com.backend.auth.repository",
"com.backend.domainoptions.repository",
"com.backend.actionlog.repository",
"com.backend.surveys.repository"
},
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager")
@EnableTransactionManagement
class PrimaryTsConfiguration {
@Autowired
Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan(
"com.backend.auth.repository",
"com.backend.domainoptions.repository",
"com.backend.actionlog.repository",
"com.backend.surveys.repository"
);
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("spring.jpa.hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean(name = "defaultDs")
@Primary
public DataSource primaryDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Bean(name="primaryTransactionManager")
@Autowired
@Primary
DataSourceTransactionManager primaryTransactionManager(@Qualifier("defaultDs") DataSource datasource) {
return new DataSourceTransactionManager(datasource);
}
}
Secondary Ts configuration:
@Configuration
@EnableJpaRepositories(basePackages = {
"com.backend.schools.repository",
"com.backend.declarations.repository",
"com.backend.verifications.repository"
},
entityManagerFactoryRef = "secondaryEntityManager",
transactionManagerRef = "secondaryTransactionManager")
@EnableConfigurationProperties(RepositoryProperties.class)
@EnableTransactionManagement
public class SecondaryTmConfiguration {
@Autowired
Environment env;
@Configuration
@PropertySource(factory = YamlPropertySourceFactory.class, value = "datasourceconfig.yml")
static class PropertiesConfiguration {
}
@Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManager(RepositoryProperties repositoryProperties) {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(secondaryDataSource(repositoryProperties));
em.setPackagesToScan(
"com.backend.schools.repository.entity",
"com.backend.declarations.repository.entity",
"com.backend.verifications.repository.entity"
);
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("spring.jpa.hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean(name = "secondDs")
public DataSource secondaryDataSource(RepositoryProperties repositoryProperties) {
System.out.println(repositoryProperties);
DataSourceRouting dataSourceRouting = new DataSourceRouting();
dataSourceRouting.initDatasource(createDataSources(repositoryProperties));
return dataSourceRouting;
}
@Bean(name="secondaryTransactionManager")
@Autowired
DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondDs") DataSource datasource) {
return new DataSourceTransactionManager(datasource);
}
private List<Pair<String, DataSource>> createDataSources(RepositoryProperties repositoryProperties) {
return repositoryProperties.getDbNumbers().stream()
.map(dbNumber -> createDataSource(dbNumber, repositoryProperties))
.collect(Collectors.toList());
}
private Pair<String, DataSource> createDataSource(String dbNumber, RepositoryProperties repositoryProperties) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(createDataSourceUrl(dbNumber));
dataSource.setUsername(repositoryProperties.getUsername());
dataSource.setPassword(repositoryProperties.getPassword());
dataSource.setDriverClassName(repositoryProperties.getDriverClassName());
return Pair.of(dbNumber, dataSource);
}
private String createDataSourceUrl(String dbNumber) {
return env.getProperty("spring.custom.datasource.url.prefix") + dbNumber + env.getProperty("spring.custom.datasource.url.suffix");
}
}
Why it's not working? What have I missed?
Solution
Ok, the problem was that JPA entities does not work with DataSourceTransactionManager
.
I used JpaTransactionManager
instead and it works fine
Answered By - SÅ‚awek Filip