For a long time, I used to implement a complicated system of checks to be able to use database transactions. The transaction logic goes as follows: open a transaction, perform the database operations, rollback upon error or commit on success. The complication comes from what happens when you want an additional operation to be performed within the same transaction. You'd either need to write a second method entirely which does both operations, or you could call your original method from a second, opening a transaction only if one hasn't already been opened and committing/rolling back changes only if you were the one to open the transaction.
For example:
public void method1() {
boolean selfOpened = false;
if(!transaction.isOpen()) {
selfOpened = true;
transaction.open();
}
try {
performDbOperations();
method2();
if(selfOpened)
transaction.commit();
} catch (SQLException e) {
if(selfOpened)
transaction.rollback();
throw e;
}
}
public void method2() {
boolean selfOpened = false;
if(!transaction.isOpen()) {
selfOpened = true;
transaction.open();
}
try {
performMoreDbOperations();
if(selfOpened)
transaction.commit();
} catch (SQLException e) {
if(selfOpened)
transaction.rollback();
throw e;
}
}
Please note, I'm not advocating the above code by any means. This should serve as an example of what not to do!
It seemed silly to create a second method to perform the same logic as the first plus something extra, yet I wanted to be able to call the database API section of the program and seal off problems there. However, while this partially solved my problem, every method I wrote involved adding this verbose logic of checking if a transaction is already open, and committing/rolling back changes if my method opened it.
The problem was conceptual. I shouldn't have attempted to embrace every possible scenario. The proper approach was to house transaction logic in a single method taking a second method as a parameter that would perform the actual database logic. That logic assumes the transaction is open and does not even perform a check. These methods could be called in combination so that these methods weren't cluttered with unnecessary transaction logic.
The reason I mention this is because my mistake was to assume that I needed to make my method work in any situation. In doing so, not only was my called method checking if a transaction was open, but also those that it called. In this case, it is not a major performance hit, but if say, I needed to verify the existence of a record in the database before proceeding, I would be checking for every method which requires it when I should have just assumed all along that the caller should be made aware that the record should exist. If the method is called anyway, this is undefined behavior and you need not worry about what happens.
Rather you should provide plenty of documentation, and write what you expect to be true before a call is made to your method. If it is important enough, add it as a comment before your method so that there should be no mistake (javadoc provides nice support for this sort of thing in java).
I hope that helps!