I have a spring boot application that connects on two databases (oracle and sql server), but my @Transactional method commits the transaction before returning to the caller. I launch my app in debug mode and realized that just after calling the .save() method, the SQLServer table already had populated.
This only happen with SQLServer tables, with oracle the tables are filled after @Transactional method returning to the caller.
I have the following configuration classes:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "br.com.getnet.consumerformalizada.repository.mssql",
entityManagerFactoryRef = "mssqlEntityManagerFactory",
transactionManagerRef = "mssqlTransactionManager"
)
public class MsSqlDataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.mssql")
public DataSourceProperties mssqlDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource mssqlDataSource() {
return mssqlDataSourceProperties()
.initializeDataSourceBuilder()
.build();
}
@Bean
public LocalContainerEntityManagerFactoryBean mssqlEntityManagerFactory(
@Qualifier("mssqlDataSource") DataSource dataSource,
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource)
.packages("br.com.getnet.consumerformalizada.model.mssql")
.build();
}
@Bean
public PlatformTransactionManager mssqlTransactionManager(
@Qualifier("mssqlEntityManagerFactory")
LocalContainerEntityManagerFactoryBean mssqlEntityManagerFactory) {
return new JpaTransactionManager(
Objects.requireNonNull(mssqlEntityManagerFactory.getObject()));
}
}
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"br.com.getnet.consumerformalizada.repository.oracle"},
entityManagerFactoryRef = "oracleEntityManagerFactory",
transactionManagerRef = "oracleTransactionManager"
)
public class OracleDataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.oracle")
public DataSourceProperties oracleDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource oracleDataSource() {
return oracleDataSourceProperties()
.initializeDataSourceBuilder().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory(
@Qualifier("oracleDataSource") DataSource dataSource,
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource)
.packages("br.com.getnet.consumerformalizada.model.oracle")
.build();
}
@Bean
@Primary
public PlatformTransactionManager oracleTransactionManager(
@Qualifier("oracleEntityManagerFactory")
LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory) {
return new JpaTransactionManager(
Objects.requireNonNull(oracleEntityManagerFactory.getObject()));
}
}
This is my @Transaction method:
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void iniciarInclusao(OpeInclusaoInput message) throws Exception {
String idOpeRgsr = message.getIdOpeRgsr();
int qtdUrs = message.getOperacao().getUrs().size();
OperacaoSantanderEntityBuilder builder = null;
//Validar se a mensagem já existe na base azure
PnrOpeSantanderEntity cttoAplicado = fmzInputService.findContratoAplicado(idOpeRgsr);
//Se não existir nenhum registro ou não estiver com a flag de aplicado, seguir com a inclusão
if (cttoAplicado == null || cttoAplicado.contratoNaoAplicado()) {
log.info("Iniciando inclusão do contrato {} com {} UR(S).", idOpeRgsr, qtdUrs);
//Salvar informações na base da azure
//Aplica os logs iniciais na base Azure
builder = new OperacaoSantanderEntityBuilder(message);
Long idOpe = fmzInputService.aplicarContrato(builder.build());
if (idOpe == null || idOpe.equals(Long.valueOf(0)))
throw new Exception("Erro ao realizar inclusão do contrato via Input Service. IdOpe: " + idOpe);
aplicarContratoPnr(message, null);
builder.updateSucessStatus();
log.info("Contrato {} incluído com sucesso.", idOpeRgsr);
}
//Se já existir registro e estiver aplicado, aborta
else {
//Não faz nada
log.info("Contrato {} já aplicado. Status {}, será ignorado.", idOpeRgsr, cttoAplicado.getStatus());
builder = new OperacaoSantanderEntityBuilder(message, cttoAplicado);
builder.updateIgnoredStatus();
}
//Atualiza os dados na base azure
this.fmzInputService.aplicarContrato(builder.build());
}
I tried to search on the web, but don't find a compatible solution.