Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect exception in AutoCloseable close()

I want to build a custom AutoCloseable class so I can turn this:

try {
    begin();
    doThings();
    commit();
} finally {
    if (transactionIsActive()) rollback();
}

into the easier

try (Transaction t = begin()) { // too bad I have to store it in t though I don't use it
    doThings();
}

Transaction would be the AutoCloseable here and in close() it would commit or rollback the transaction as appropriate.

But to make that work, I would need to detect in Transaction.close() whether an exception occurred inside the try block or it completed normally. Is this possible at all?

If it requires parsing the stack trace from a new exception, that's OK. Easier programming would be worth the tiny performance hit that brings.

like image 218
Bart van Heukelom Avatar asked Dec 04 '11 15:12

Bart van Heukelom


2 Answers

The closest I could come up with still requires marking the success of the transaction manually as the last statement of the block:

class Transaction implements AutoCloseable {
    private boolean rollback = true;

    public void success() {
        rollback = false;
    }

    public void close() {
        if (rollback) doRollback();
        else doCommit();
        // …
    }
}

class Main {
    public static void main(String[] args) {
        try (Transaction t = new Transaction()) {
            doThings();
            t.success();
        }
    }
}
like image 102
millimoose Avatar answered Oct 24 '22 18:10

millimoose


Although my code is different from yours, I had a similar need to automatically commit (most) transactions and rollback on errors.

Most of the time my code is sprinkled with simple queries that get rolled back automatically, like this:

try(Transaction t : database.beginTransaction()) {
  return t.selectUnique(Employee.class, "id=?", 200);
}  // implicit rollback here

Some databases donot like queries being rolled back like this, so I've solved this by distinguishing between "write" and "read" transactions. If it is a read transaction, close() will commit it, otherwise rollback. It will also check you are not doing any write actions when you created a read-only transaction. So now I can write:

try(Transaction t : database.beginReadOnlyTransaction()) {
  return t.selectUnique(Employee.class, "id=?", 200);
}  // implicit commit here

Write transactions still need to call commit themselves at the end, but this is a minority of cases.

I realize this is not what you asked for, but perhaps it is useful still.

like image 28
john16384 Avatar answered Oct 24 '22 16:10

john16384