2

I have a Spring Boot 2.3 REST application with a standard architecture (controller -> service -> repository). For auditing purposes, I inserted a thin layer (some kind of a Mediator), so that I persist all the requests to some specific service method regardless they are successfully persisted or an exception is thrown and the transaction is rollbacked. Example:

@Component
public class Mediator {

    private final Service service;
    private final AuditService auditService;

    public Mediator(Service service, AuditService auditService) {
        this.service = service;
        this.auditService = auditService;
    }

    @Transactional
    public void saveReport(Report report) {
        try {
            service.saveReport(report);
            auditService.saveReport(report); 
        } catch (Exception exception) {
            auditService.saveReport(report, exception);
            throw exception;
        }
    }
}

Thus I encountered a weird situation: if I place the @Transactional on the Mediator's method (example above), all the operations in the JPA Repositories are successfully persisted. If I place the @Transactional on the ServiceImpl method instead (example below) and no longer on the Mediator, one of the delete queries is not ran, although the rest of the queries are executed just fine. Suppose my ServiceImpl looks something like:

@Service
public class ServiceImpl implements Service {

    private final RepositoryA repositoryA;
    private final RepositoryB repositoryB;

    public ServiceImpl(RepositoryA repositoryA, RepositoryB repositoryB) {
        this.repositoryA = repositoryA;
        this.repositoryB = repositoryB;
    }

    @Transactional
    public void saveReport(Report report) {
        repositoryA.save(report.getA());
        repositoryB.save(report.getB());
        repositoryB.delete(report.getSomethingElse());
    }
}

The only visible difference between the two approaches with respect to the Transactions is that in the first scenario, I have this for each Mediator call:

o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(909894553<open>)] for JPA transaction

while in the second scenario, I have this:

tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation

I guess there is a difference between annotating directly a bean's method with @Transactional (what I do in the Mediator) and annotating a bean's (that is the implementation of the interface injected) method with the same thing (what we usually do by annotating a ServiceImpl method), but I am not sure and cannot spot the reason for this weird behaviour. Does anyone have an idea why this happens?

Daniel Pop
  • 456
  • 1
  • 6
  • 23
  • 2
    Kindly see the following: https://stackoverflow.com/questions/8224465/use-of-proxies-in-spring-aop/8224772#8224772 https://stackoverflow.com/questions/17772614/override-and-transactional-annotations – JCompetence Oct 18 '21 at 12:29
  • @SusanMustafa those questions help explain the difference between a bean that implements an interface and one that does not, but I can't spot the reason of the weird behaviour in my application – Daniel Pop Oct 18 '21 at 12:42
  • It might be because you are using Repositories directly and therefore it uses the SharedEntityManagerInvocationHandler. You could test this theory by having your Mediator call the repositories directly? Instead of calling Service..and see if you get the same. That way we could eliminate the possibility of having implements interface as a factor in this situation. Still very interesting, and I would love to know what you get. – JCompetence Oct 18 '21 at 12:57
  • @SusanMustafa in the real-world example, the call to the repositories' methods is done through a call to another service, which is injected with the one annotated (or not) with Transactional. I made the to the DAO method directly in the first Service (the one in the example) but still to no avail. The entity remains in the db. – Daniel Pop Oct 19 '21 at 05:47
  • Do you use an EntityManager with @PersistenceContext or its the default jpa entitymanager? – Bored Oct 21 '21 at 14:00
  • @Bored it's the default jpa entitymanager, no bean defined for this in the whole app – Daniel Pop Oct 22 '21 at 11:08

1 Answers1

0

I guess that this difference in behavior is due to Spring OpenSessionInView which is enabled by default. You must set in application.yml

spring:
  jpa:
    open-in-view: false

Please see OSIV

Bored
  • 499
  • 4
  • 11