Savepoint
Ebean supports using Savepoints
which are a feature of most Relational Databases
in which we can create a Savepoint (nested transaction) inside a transaction which can
commit or rollback as a unit of work.
If we have a task that performs some operations which can fail/rollback, but don't
want to loose all the work for the entire transaction Savepoint
provides a mechanism to do this.
You can check more information on Oracle's JDBC tutorial
Transaction.setNestedUseSavepoint()
For a transaction we can use transaction.setNestedUseSavepoint()
to enable it to use
Savepoint
for nested transactions. This means that these nested transactions
can be rolled back leaving the outer transaction to continue and potentially commit.
Example
// start 'outer' transaction
try (Transaction outerTxn = database.beginTransaction()) {
outerTxn.setNestedUseSavepoint();
// do stuff with the 'outer' transaction
bean.save();
try (Transaction nestedTransaction = database.beginTransaction()) {
// nested transaction is a savepoint ...
// do some piece of work which we might want to either commit or rollback ...
otherBean.save();
if (...) {
nestedTransaction.rollback();
} else {
nestedTransaction.commit();
}
}
// continue using 'outer' transaction ...
outerTxn.commit();
}
Explicit Savepoints
As an alternative to using transaction.setNestedUseSavepoint() we can instead
explicitly create and use savepoints by obtaining the JDBC Connection
from the transaction.
For example:
var newCustomer = new Customer();
newCustomer.setName("John");
newCustomer.save();
try (Transaction transaction = DB.beginTransaction()) {
Connection connection = transaction.getConnection();
// create a Savepoint
Savepoint savepoint = connection.setSavepoint();
newCustomer.setName("Doe");
newCustomer.save();
// Rollback to a specific save point
connection.rollback(savepoint);
transaction.commit();
}
var found = DB.find(Customer.class, newCustomer.getId());
System.out.println(found.getName()); // Prints "John"