1

We've multiple dependent APIs that are calling inside loops and we need to persist each APIs response in one Audit table. Now, we're facing an issue like if the first API is throwing some exception (400/500...) we need to store at least audit data in the audit table but because of @Transactional it is rollicking audit table data also and we don't want to rollback those data. Please give needful suggestions. We tried this but no luck @Transactional(propagation = Propagation.NEVER) ,

@Transactional(propagation = Propagation.NOT_SUPPORTED) . For NOT_SUPPORTED, it's stopped to proceed further.


@Transactional
public void apiImplementation() {
   auditService.addDataInAuditTable("123","...");
   boolean isLastPage = true;
   int currentPage = 0;
   do {
    ResponseEntity<AbcModel> response = restApiService
            apiCallToGetAbcDetails(100, currentPage++);
    if (response != null && response.getStatusCode() == HttpStatus.OK) {
        AbcModel abcModel = response.getBody();
        if (abcModel != null) {
        isLastPage = abcModel.isLastPage();
        abcModel.getData.forEach(this::secondApiImplementation);
         }
    }
    } while (!isLastPage);
}               
    
private void secondApiImplementation(AbcModel abcModel) {
    here we're saving data and calling the second Api... and also save data in auditlog for second api
}
Vy Do
  • 46,709
  • 59
  • 215
  • 313
M123
  • 1,203
  • 4
  • 14
  • 31
  • The code for your audit should be in a different bean, honestly. You have a mix of concerns now. But if you want to go ahead and keep it as it is now, you can drop the `@Transactional` annotation and start using the `TransactionTemplate` instead. – Nico Van Belle May 31 '23 at 11:14

1 Answers1

0

This is a typical use case for spring-aop.

If you have only one way to handle the error of all methods, essentially you will define your audited method with @Auditable

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable{}

@Transactional
@Auditable
public void apiImplementation(){
  // Stuff
}

And then in your Aspect

@Aspect
@Component
public class AuditAspect {

  @Around("@annotation(auditable)")
  public void audit(ProceedingJoinPoint joinPoint) throws Throwable {
    try{
      // You can audit before if you would like
      joinPoint.proceed();
    } finally {
    //Do your thing here (call another service/repository or private methods). You will audit regardless of the method execution.
    }
  }
}

You can also capture the result of proceed() if you want to audit also the response of your API. If you want to retrieve the runtime arguments of your api methods, you can probably use this answer

Please note that spring-aop will only work with proxied methods. It wont intercept any private secondApiImplementation().

Julien Antony
  • 131
  • 2
  • 5
  • I'm not sure it's a great idea to use an entitymanager that may already have thrown an exception. The hibernate documentation says once the session throws an exception it needs to be discarded. – Nathan Hughes Jun 01 '23 at 13:32
  • I believe that the session might not be an issue. If I use the same session (request thread), and call 2 Transactional methods with REQUIRED_NEW method aside from each other, it should just work as expected, even if one method throws an exception ? (genuine question here, not rethorical) – Julien Antony Jun 01 '23 at 13:53