I'm facing a situation I can't even find a way to debug.
I have the following spring-data
repositories:
IntegrationLogRepository
public interface IntegrationLogRepository extends PagingAndSortingRepository<IntegrationLog, Long> {
}
FooRepository
public interface FooRepository extends PagingAndSortingRepository<Foo, Long> {
}
And on my business logic I have something like the following:
IntegrationLog log = new IntegrationLog();
log.setTimestamp(new Date());
try {
Foo foo = new Foo();
// Build the Foo object...
fooRepository.save(foo);
log.setStatus("OK");
} catch (Exception e) {
log.setStatus("NOK");
} finally {
integrationLogRepository.save(log);
}
When the integrations runs fine, the log
is saved with the OK
status. Everything is fine. But when I have an exception, for some reason integrationLogRepository.save(log)
doesn't do nothing. I mean nothing NOTHING: No exception is thrown and I can't see any hibernate query being executed on my WebLogic console. The log is not persisted...
Any ideas on why this is happening?
Following are my dependencies:
compile 'org.springframework.boot:spring-boot-starter-data-rest'
compile 'org.springframework.boot:spring-boot-starter-security'
compile "org.springframework.boot:spring-boot-starter-web-services"
runtime 'org.springframework.boot:spring-boot-devtools'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile "org.springframework.boot:spring-boot-starter-websocket"
compile 'javax.servlet:javax.servlet-api:3.1.0'
compile 'org.hibernate:hibernate-core:5.1.16.Final'
compile 'org.hibernate:hibernate-validator:5.2.3.Final'
compile 'org.hibernate:hibernate-entitymanager:5.1.0.Final'
Running on Spring Boot 1.5.15.RELEASE
, Java 1.7
and WebLogic 12.1.3
.
Thanks!
The exception that was thrown is also rolling back the integration log save. If you want to have the log saved, you have to give it a separate transaction when saving.
Abstract the log repository to a service and on the service method that saves the log add the transaction that creates a new transaction.
@Service
public class IntegrationLogService {
private final IntegrationLogRepository logRepository;
@Autowired
public IntegrationLogService(IntegrationLogRepository logRepository) {
this.logRepository = logRepository;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log) {
this.logRepository.save(log);
}
}
In your business replace
finally {
integrationLogRepository.save(log);
}
with
finally {
integrationLogService.save(log);
}
Edit
Why setting @Transactional(propagation = Propagation.NOT_SUPPORTED)
on the business layer worked?
To understand why it worked, we need to first look at what happens when one calls save
on a repository in spring that uses org.springframework.data.repository.CrudRepository
.
Spring tries to determine the TransactionAttribute
's on the method and targetClass. For the method save
and class CrudRepository
, in short it did not find any. Spring uses SimpleJpaRepository
as the default implementation of the CrudRepository
, if it did not find any transactional attributes on yours it will use the one specified in SimpleJpaRepository
.
@Transactional
public <S extends T> S save(S entity)
The default propagation on @Transactional
is required.
Propagation propagation() default Propagation.REQUIRED;
Support a current transaction, create a new one if none exists.
As you can see from the documentation above, if no transaction is specified it will create a new one. So when you set the transaction on the business layer to NOT_SUPPORTED
(Execute non-transactionally), the actual CrudRepository
did create it's own transaction which means the rollback would not affect it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With