40

I wonder whether it makes sense to use instead of

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

to use Throwable

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)

As I understand catching Error will help us behave correctly even when something really bad happen. Or maybe it wouldn't help?

Igor Konoplyanko
  • 9,176
  • 6
  • 57
  • 100

4 Answers4

58

As I understand catching Error will help us behave correctly even when something really bad happen. Or maybe it wouldn't help?

You don't need to explicitly specify rollbackFor = Throwable.class, because spring will by default rollback the transaction if an Error occurs.

See 1.4.3. Rolling Back a Declarative Transaction

In its default configuration, the Spring Framework’s transaction infrastructure code marks a transaction for rollback only in the case of runtime, unchecked exceptions. That is, when the thrown exception is an instance or subclass of RuntimeException. (Error instances also, by default, result in a rollback). Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.

Or take a look at the DefaultTransactionAttribute

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}
Marc Bannout
  • 388
  • 4
  • 15
René Link
  • 48,224
  • 13
  • 108
  • 140
  • my parent table sometime doesn't update:https://stackoverflow.com/questions/73091178/data-inconsistancy-parent-table-is-not-updated-while-child-table-is-updated – Hacke Jul 26 '22 at 07:23
16

Since you are using @Transactional, we can safely assume you are doing your database operations through Spring, Hibernate, or other JDBC wrappers. These JDBC wrappers don't typically throw checked exceptions, they throw runtime exceptions that wrap the JDBC SQLException types.

@Transactional is setup to rollback, by default, only when an unchecked exception is thrown.

Consider a use case like so

@Transactional
public void persistAndWrite(Bean someBean) throws IOException {
    // DB operation
    getSession().save(someBean); 

    // File IO operation which throws IOException
    someFileService.writeToFile(someBean); 
}

You wouldn't necessarily want to rollback the DB operation just because we couldn't write something to a file.

Similarly

@Transactional
public void persistAndThrowOutOfMemory(Bean someBean)  {
    // DB operation
    getSession().save(someBean);

    // consumes all memory, throws OutOfMemoryError
    someService.hugeOperationThrowsOutOfMemoryError(); 
}

You wouldn't necessarily want to rollback the saved entity just because some service cause too much memory to be consumed.

@Transactional gives you the option. Use it where appropriate.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 4
    If I marked this method as transactions - then I'm sure that I want to rollback on some hard error. But If it will generally work and considered good practice? – Igor Konoplyanko Jan 17 '14 at 15:06
  • @igor It will rollback on runtime exceptions and whatever else you tell it to rollback on. It's not generally good practice to catch anything other than `Exception` and `RuntimeException` types. – Sotirios Delimanolis Jan 17 '14 at 15:08
  • 1
    Why it is not a good practice to try to rollback on Error/RuntimeException ? – Guillaume Mar 27 '15 at 08:18
  • @Guillaume It will rollback on `RuntimeException`. You shouldn't try to handle `Error` because they are generally thrown by the JVM and are unrecoverable. – Sotirios Delimanolis Mar 27 '15 at 14:41
  • 2
    But by default transactions will be rolled back on an `Error` too. At least both JTA and Spring rollback on a `RuntimeException` **and** an `Error`, unless you explicitly specify `dontRollbackOn=Error.class`. – Yoory N. Jun 23 '19 at 09:33
2

Default value of rollback is register on Error Exception but when u register try{}catch{} manually it overriding the error, so in this case use

catch {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }

to do it manually or remove try catch

also you may register exception type in transactional annotation such:

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
Ali.Mojtahed
  • 2,537
  • 1
  • 15
  • 23
0

I don't know is it possible or not but handling Throwables like Errors is a bad style of programming, it is not the developers responsibility to handle this kind of fatal errors. There always can happen bad things which You cannot handle. You should handle checked exceptions if necessary, which are known to your system like some type of logical errors.

Arsen Alexanyan
  • 3,061
  • 5
  • 25
  • 45