Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving does not work when exception is handled

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!

like image 538
João Menighin Avatar asked Dec 20 '18 12:12

João Menighin


1 Answers

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.

like image 193
Rentius2407 Avatar answered Oct 22 '22 08:10

Rentius2407