5

For a mysql connection I have a connection object and use a transaction mechanism connection.startTransaction(), connection.commitTransaction(), connection.rollbackTransaction().

For each startTransaction(), there must always be either a call to commitTransaction() or to rollbackTransaction(). Missing such a call or calling both would break my transaction system.

So I use them in the following way:

boolean i_am_in_a_transaction=true;
try {
    connection.startTransaction();
    ...
    i_am_in_a_transaction=false;
    connection.commitTransaction();
} finally {
    if(i_am_in_a_transaction) {
        connection.rollbackTransaction();
    }
}

This ensures the declared calling order, but it is much effort, because I have to write this lines everywhere where I use transactions.

In C++ I would use a transaction object that checks in its destructor, if the commit() function was called, and that otherwise calls rollback():

class Transaction {
    public:
        Transaction()
            :am_in_transaction(false) {
        }

        ~Transaction() {
            if(_am_in_transaction) {
                rollback();
            }
        }

        void startTransaction() {
            _am_in_transaction=true;
            ...start Transaction...
        }

        void commit() {
            _am_in_transaction=false;
            ...commit Transaction...
        }

        void rollback() {
            _am_in_transaction=false;
            ...rollback Transaction...
        }

    private:
        bool _am_in_transaction;
}

This way I have the logic implemented at one place and can use it really simple:

Transaction my_transaction;
my_transaction.startTransaction;
...
my_transaction.commit();

This code is much simpler than the above java code with the try/finally block.

Is there a way to implement this behaviour in java without dedicating the logic to the caller and making him implement the try/finally block?

Something like a way to automatically call a function on scope exit would help me.

Adam Wagner
  • 15,469
  • 7
  • 52
  • 66
Heinzi
  • 5,793
  • 4
  • 40
  • 69
  • 1
    possible duplicate of [Does java have a using clause?](http://stackoverflow.com/questions/141241/does-java-have-a-using-clause) – Dark Falcon Oct 27 '11 at 14:30
  • 2
    @sgusc Java 7 will automatically close resources but not automatically invoke an unknown function – John Vint Oct 27 '11 at 14:34
  • @JohnVint Yeah, good point. I forgot the semantics of `using`. I was equating it to Python's `with` statement in my head. Thanks for clarifying! – Brent Writes Code Oct 27 '11 at 14:42
  • possible duplicate of [Java 7 Automatic Resource Management JDBC](http://stackoverflow.com/questions/9260159/java-7-automatic-resource-management-jdbc) – Raedwald Apr 23 '15 at 06:00

6 Answers6

5

Something similar (very important) to destructors in C++ is the method finalize(). The difference is that there is no guarantee when the garbage collector will actually call it, so it's not recommendable to rely on it.

Otherwise, the best you can do is try/finally blocks.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • more, it will call it at max once. So even if you rescue the object the next time will not execute the finalize. – ssedano Oct 27 '11 at 14:33
  • finalize() is not a destructor. JVM is responsible to call it. It calls it when it wants if any. So, it is highly not recommended to base any logic on finalize(). It can be used for fall backs only. – AlexR Oct 27 '11 at 16:39
5

Java does not have destructors. But there are several "standard" solutions that can help you.

1.Use pattern named "template method".

abstract class Base {
    public query() {
         openTransaction();
         doQuery();
         closeTransaction();
    }
    protected abstract doQuery();
}

Now you should implement the doQuery() in each subclass you create and use it by invoking 'query()' from the base class.

2.Use aspect oriented programming

3.Use Decorator (Wrapper) pattern.

4.Use one of the popular ORM frameworks (Hibernate, iBatis etc) that solve all these problems for you and do not deal at all with the low level JDBC stuff.

AlexR
  • 114,158
  • 16
  • 130
  • 208
4

A couple solutions come to mind...

As @Dark Falcon points out, this would be a good use case for a try with resources call that will automatically clean up your resources at the end of the try. Unfortunately, this is only available in Java 7.

Java classes do define a finalize() method that can get called when the object gets garbage collected, but overriding this method is almost never the right thing to do.

I think your only other option if you're hooked on this idea of "execute code on function return" is to using Aspect Oriented Programming. If you read up on some packages like AspectJ or look into use AOP with Spring, you can do some configuration magic to get code to execute when a function returns by intercepting the call. Here's an example of using Spring AOP to execute other code when your function returns.

Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
2

If update to java 7 is an option, there are the new try with resources that will execute the method close in a Closable implementation.

ssedano
  • 8,322
  • 9
  • 60
  • 98
1

I would have just one method and do it only once.

public static void update(Connection connection, String updateSQL) {
    PreparedStatement update = null;
    try {
      try {
        connection.startTransaction();
        update = connection.prepareStatement(updateString);
        update.executeUpdate();
      } finally {
        connection.rollbackTransaction();
      }
    connection.commitTransaction();
    } finally {
        if(update != null) update.close();
    }
}

later

update(connection, updateSQL1);
update(connection, updateSQL2);
// etc.
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I don't really understand your code (finally is always executed - also when no exception was thrown), but I think I got your idea. This works when having only one sql statement inside of the transaction. But a transaction often consists of multiple statements. – Heinzi Oct 27 '11 at 16:01
  • In which case you can pass a List or `String...` of SQL statements. – Peter Lawrey Oct 27 '11 at 21:16
  • except they depend on each other – Heinzi Oct 27 '11 at 23:01
  • Not sure how the updates can depend on each other. Are some executed conditionally based on whether others fail, but you can't write this in SQL? But if you need this you can call update() repeatedly. – Peter Lawrey Oct 28 '11 at 07:31
  • you're right - when there are only update statements, this approach works. – Heinzi Oct 29 '11 at 23:08
0

I know this is an old question, but for the benefit of others:

You can use an anonymous inner class that implements an interface to do the work, similar to the way the Java Comparator and List sort mechanisms work. This allows you to treat the inner class as if it were an execution scope.

e.g. Modifying your original example:

class Transaction
{
   boolean i_am_in_a_transaction=false;

   interface AutoRollback
   {
       void runQueries() throws Throwable;
   }

    void startTransaction() {
        i_am_in_a_transaction=true;
        ...start Transaction...
    }

    void commit() {
        i_am_in_a_transaction=false;
        ...commit Transaction...
    }

    void rollback() {            
        i_am_in_a_transaction=false;
        ...rollback Transaction...
    }

   public void execute(AutoRollback work)
   {
      try {
          work.runQueries();
      } catch ( Throwable t ) {
          rollback();
          throw t;
      }
   }
}

And then an example of how to use it:

void test() throws WhateverException
{
    Transaction my_transaction;
    my_transaction.startTransaction();

    my_transaction.execute( new AutoRollback() { public void runQueries() throws Throwable {

     ... perform your queries: can be more than one, complex code, etc. ...
     ... local variables from the enclosing scope can be used as long as they are final... 

    }});

    my_transaction.commit();
}

If you have Java 8, this becomes much prettier with lambdas as it saves the new AutoRollback syntax.

If you don't have Java 8 and the excess punctuation still bothers you, you should be able to use an annotations processor and code injection to make it read pretty. A compile-time annotation with the target set to LOCAL_VARIABLE is what you want, then apply it to my_transaction.

...Assuming annotations processors such as apt or pre-processors are allowable at your workplace, and you want to do that much work for syntactic sugar.

lisa
  • 1,292
  • 12
  • 8