1

I would like to ignore the exception and commit the transaction anyway. However, every time I get a error. What exactly is the problem and how can I solve it?

@Transactional( noRollbackFor = EmptyResultDataAccessException.class )
   public Foo foo( Foo foo, String barId ) {
    Foo foo = fooRepository.save(foo)
    deleteBarSilently(barId)
      return foo ;
   }


   //@Transactional( noRollbackFor = EmptyResultDataAccessException.class )
   private void deleteBarSilently( final String barId) {

      try {
         barRepository.deleteById( barId);
      } catch ( final EmptyResultDataAccessException ignored ) {
         System.out.println( ignored );
      }
   }

Error is: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction "rolled back" because transaction was set to RollbackOnly.

Exception is EmptyResultDataAccessException

EDIT: Also tried it with try/catch and without and with a array of exception @Transactional( noRollbackFor = {EmptyResultDataAccessException.class} ) and with fullqualified class name

Log without catch

Application exception overridden by commit exception

org.springframework.dao.EmptyResultDataAccessException: No class com.bosch.bci.foundation.digitaltwin.repository.domain.ModelReference entity with id c16675da-f5ce-46b1-97e6-7eb6869940aa exists!
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.lambda$deleteById$0(SimpleJpaRepository.java:166) ~[spring-data-jpa-2.5.3.jar:2.5.3]

Log with catch is just

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction "rolled back" because transaction was set to RollbackOnly.
MelleD
  • 657
  • 1
  • 9
  • 23

1 Answers1

2

noRollbackFor expects an array of Throwable subclasses instead you are passing only a class (EmptyResultDataAccessException)

Try changing to (note the curly braces "{" and "}" which signify the array):

@Transactional( noRollbackFor = { EmptyResultDataAccessException.class })

UPDATE

It seems that since EmptyResultDataAccessException is a RuntimeException trying not to rollback upon hitting it is not a case, unless you globally set globalRollbackOnParticipationFailure=false.

Solution 1

The first solution is to implement your own barRepository.deleteById( barId) which does not throw EmptyResultDataAccessException

e.g.

in your BarRepository create the following method:

@Transactional
@Modifying
@Query("DELETE FROM Bar b WHERE b.id= :id")
void customDeleteById(Long id);

and in your deleteBarSilently remove the try catch and simply have:

@Transactional
private void deleteBarSilently( final Long barId) {
     barRepository.customDeleteById( barId);
}

Solution 2

In the second solution you can change your code to the following to run the deleteBarSilently outside of the transaction of foo so when you hit the EmptyResultDataAccessException simply try catch it and no rollback will occur.

Firstly you need to separate your methods to different services (for @Transactional annotation to work), lets say FooService and BarService,

FooService

@Service
public class FooService {

    @Autowired
    private FooRepository fooRepository;

    @Autowired
    private BarService barService;

    @Transactional
    public Student foo( Foo foo, Long barId ) {
        Foo savedFoo = fooRepository.save(foo);
        barService.deleteBarSilently(barId);
        return savedFoo ;
    }
}

BarService

Annotate the method with @Transactional(propagation = Propagation.NOT_SUPPORTED) which will cause the transaction to suspend, execute the method's code outside of transaction and then resume.

@Service
public class BarService {

    @Autowired
    private BarRepository barRepository;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void deleteBarSilently( final Long barId) {
        try {
            barRepository.deleteById( barId);
        } catch (EmptyResultDataAccessException exc) {
            System.out.println(exc);
        }
    }
}
pleft
  • 7,567
  • 2
  • 21
  • 45
  • Unfortunately that makes no difference. I've already tested that. I've also tried it with and without try / catch. – MelleD Sep 02 '21 at 14:49
  • Yeah no luck with `noRollbackFor`. I have updated my answer with 2 possible solutions that I have tested and they work, please have a look at it. – pleft Sep 03 '21 at 08:25
  • Thanks for the detailed answer. I know both solutions, but I don't like them. The third option is to call findById(barId).ifPresent(em: remove) so that no exception happens. You can add this solution as well in your post. Then i think all solutions are in one post. 2 questions: 1. Is there a note in the documentation that I overlooked that no RuntimeExeptions work? Most exceptions are runtime exceptions, aren't they?Is there a reason for the restriction 2. Can I catch the RuntimeException and convert/rethrow a checked exception, which is specified in noRollbackFor. Would it work? – MelleD Sep 04 '21 at 07:45
  • do you have some ideas or hint here? – MelleD Sep 09 '21 at 13:07
  • Have you tried what I mentioned in my answer: "It seems that since `EmptyResultDataAccessException` is a `RuntimeException` trying not to rollback upon hitting it is not a case, unless you globally set `globalRollbackOnParticipationFailure=false`.". Have you set this property to `false` and see if `noRollbackFor` works as you want? I believe you can easily overcome your problem with applying either my suggestions or the ones in your comment above. – pleft Sep 09 '21 at 13:44
  • If you want to learn why it does not work as you expect, I believe the reason still is `globalRollbackOnParticipationFailure`, you can read more in relevant questions here in SO e.g. https://stackoverflow.com/questions/27849968/transactional-norollbackfor-runtimeexception-class-does-not-prevent-rollback or try to debug into spring transaction internals. The rule however is: `Any RuntimeException triggers rollback, and any checked Exception does not.` https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative-attransactional-settings – pleft Sep 09 '21 at 13:47
  • I was just wondering or the documentation is misleading 'Any RuntimeException triggers rollback, and any checked Exception does not' but I understand: This is the default behavior and if I set the noRollbackFor i override the default the behavior. Didn't try the globalRollbackOnParticipationFailure. I thought with globalRollbackOnParticipationFailure=false, the consequence is i have to handle all other exception manually to rollback the transaction? This was not my intention... – MelleD Sep 10 '21 at 11:25