An important matter in a transaction is to specify how we handle an exceptional condition when we are adding, updating, or deleting data in our database; in some cases, we might be doing data operations in several tables as a set of related data, and if, for some reason, we have an error like a checked or unchecked exception we need to rollback all the data we already modified, so we can avoid incomplete or duplicated entries in our database and retry the operation

The @Transactional annotation

Spring, then, contains a useful annotation to help us to wrap all of our steps from a method in a database transaction: @Transactional, which also contains, along with a lot more options, a propagation attribute that will allow us to specify the behavior for our method in a transactional space and we can choose between the following options to define the propagation:

  • REQUIRED: The method will use an existent transaction if any, or it will create a new one; by default, if an exception occurs, the whole transaction will be a rollback, including the operations performed by the caller.
  • REQUIRES_NEW: The operation will open a new transaction every time is called. If a transaction is already open, it will pause the current transaction and execute the new transactional block; once it is finished, the original transaction can continue. Be aware that unless you handle the un-checked exception internally, the original transaction will roll back too because the runtime exception will end your current execution.
  • NESTED: Allows to execute our code in an existent transaction but establishing checkpoints so a method that fails can do a rollback independently of an outer transaction.
  • MANDATORY: This propagation states that a transaction must exist to execute our code; if there’s no open transaction, an exception will be thrown.
  • NEVER: Contrary to MANDATORY, if a transaction exists when our code is executed, an exception will be thrown.
  • NOT_SUPPORTED: If there’s an open transaction when our method is called, the transaction manager will pause the transaction, our code will be executed in a non-transactional space. Once it is finished, the transaction will resume.
  • SUPPORTS: The method will be executed in a transaction (if any) or non-transactional if there’s no transaction open.

Rollback on checked exceptions

Depending on your business logic, you may want to roll back a transaction if a checked exception is thrown, so a RuntimeException (or a child of this exception) needs to occur to trigger a rollback. In that case, we can use the rollbackFor argument in the @Transactional annotation to specify a single or an array of classes that inherits of Throwable, keep in mind that any exception that inherits of RuntimeException will trigger a rollback too.

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyCustomException.class)
    public void insertNewGame(Game game) throws Exception {