@Transactional(isolation = Isolation.READ_COMMITTED)
void a(){b();}
@Transactional(isolation = Isolation.SERIALIZABLE)
void b(){}
DataSourceUtils.getConnection(ApplicationContextProvider.context().getBean(DataSource.class))
ApplicationContextProvider.context().getBean(JdbcTemplate.class).queryForList("SELECT * FROM ATM_IMPORTED_FILE")
Sim, se voce fizer um update no hibernate e select no jdbc template eles vao usar a mesma transacao e só vai commitar no final Só não vai refletir entre o JDBCTemplate e o hibernate se o Hibernate rodar um HQL por exemplo e não rodar o flush
Se a transação estiver marcada para rollback ou se a sincronizacao de transacao nao estiver ativa então o registro de sincronizacao vai estourar excecao, pode ser util verificar antes:
if(TransactionSynchronizationManager.isSynchronizationActive()){
TransactionSynchronizationManager.registerSynchronization(....);
}
....
@Transactional
public void createFile(File file){
TransactionSynchronizationManager.registerSynchronization(new FileDeletioner(file));
// filedao.persist()...
}
public class FileDeletioner extends TransactionSynchronizationAdapter {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final File file;
FileDeletioner(final File file) {
this.file = file;
}
@Override
public void afterCompletion(final int status) {
logger.info("file={}, status={}", file, status);
switch (status) {
case STATUS_COMMITTED:
logger.info("file={}, deleted={}", file, file.delete());
break;
}
}
}
Criar um rabbit template manual e setar set channel transacted, então provavelmente irá funcionar o controle transacional
Manualmente comita ou da rollback nas transacoes
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// execute your business logic here
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
Automaticamente comita ou da rollback nas transacoes
@Autowired
private PlatformTransactionManager platformTransactionManager;
final TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
transactionTemplate.execute((TransactionCallback) status -> {
final JobResult result = this.processBatchClosing(currentDate);
bbSplitterService.splitLastBatches(currentDate);
withdrawBatchSummaryService.createClosedAndNotSentBatchSummaries();
return null;
});
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
logging.level.org.springframework.transaction=TRACE
logging.level.org.springframework.jdbc=DEBUG
spring.datasource.tomcat.max-active=4
spring.datasource.tomcat.max-idle=2
spring.datasource.tomcat.min-idle=1
spring.datasource.tomcat.initial-size=1
Nesse caso ele vai reutilizar a transacao e só vai comitar quando sair do metodo update.
@Transactional(propagation = Propagation.REQUIRED)
public void update(){
final TransactionTemplate template = new TransactionTemplate(txManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.execute(status -> {...})
}
Vai suspender a transacao criada no metodo e vai criar uma nova para cada chamada ao .execute
e ja vai comitar refletindo no banco
@Transactional(propagation = Propagation.REQUIRED)
public void update(){
final TransactionTemplate template = new TransactionTemplate(txManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
for(1 a 10) template.execute(status -> {...})
}
Por mais que seja required, como o execute está dentro de um for
ele vai criar uma transação por chamada ao .execute
já comitando quando sair do mesmo
@Transactional(propagation = Propagation.NEVER)
public void update(){
final TransactionTemplate template = new TransactionTemplate(txManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
for(1 a 10) template.execute(status -> {...})
}
Baiscamente voce faria assim
@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = DuplicateKeyException.class)
public void createCustomerWithoutFail(CustomerEntity customer) {
customerDAO.create(customer);
}
E faria um for nele
@Transactional
public void createCustomersWithoutFail(List<CustomerEntity> customerEntities) {
for(CustomerEntity customerEntity: customerEntities){
customerService.createCustomerWithoutFail(customerEntity);
Porém o postgres não aceita que uma transação que recebeu DuplicateKeyException
possa ser comitada, recebendo a seguinte exceção:
Caused by: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block
Então existem duas opções:
Solução 1 - Solução transacional
@Transactional
public void createCustomersWithoutFail(List<CustomerEntity> customerEntities) {
for(CustomerEntity customerEntity: customerEntities){
try {
TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
transactionTemplate.execute(ts -> {
customerService.createCustomerWithoutFail(customerEntity);
return null;
});
} catch (final DuplicateKeyException e) {
LOGGER.warn("status=duplicated, name={}, msg={}", customerEntity.getFirstName(), e.getMessage(), e);
}
}
}
Dessa forma com a NESTED
se ela rececber a duplicated só ela será abortada e o pai ficará intacto mas se a transação pai levar exceção logo levará rollback então todas terão, diferente da requires_new
Solução 2 - Não funciona
Dentro do createCustomerWithoutFail
fazer o for
chamando o DAO
. Vai dar na mesma a transação já foi abortada no banco, voce não pode reutiliza-la
Solução 3 - Solução de banco
Simplemente faça um INSERT WHERE NOT EXISTS
ou faça um select antes e veja se existe um registro correspodente
aqui o SQL do create
do DAO
INSERT INTO tag ("key", "value")
SELECT 'key1', 'value1'
WHERE NOT EXISTS (
SELECT id, "key", "value"
FROM node_tag
WHERE key = 'key1' AND value = 'value1'
) returning id, "key", "value"
Como voce ve no exemplo o transacional funciona mas tem uma limitação, uma vez que voce executa o TransactionTemplate ele ja comita em seguida, entao mesmo que voce use o required, nao vai conseguir reutilizar a transacao.