Java:在作用域出口自动调用函数(如c ++析构函数)

时间:2011-10-27 14:26:28

标签: java jdbc

对于mysql连接,我有一个连接对象,并使用事务机制connection.startTransaction()connection.commitTransaction()connection.rollbackTransaction()

对于每个startTransaction(),必须始终呼叫commitTransaction()rollbackTransaction()。错过这样的电话或同时打电话都会破坏我的交易系统。

所以我按照以下方式使用它们:

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();
    }
}

这确保了声明的调用顺序,但是要付出很多努力,因为我必须在我使用事务的地方写这些行。

在C ++中,如果调用了commit()函数,我将使用检查其析构函数的事务对象,否则调用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;
}

这样我就可以在一个地方实现逻辑,并且可以非常简单地使用它:

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

这段代码比上面带有try / finally块的java代码简单得多。

有没有办法在java中实现这种行为而不将逻辑专用于调用者并让他实现try / finally块?

在范围退出时自动调用函数的方法会对我有所帮助。

6 个答案:

答案 0 :(得分:5)

C ++中析构函数的类似(非常重要)是方法finalize()。不同之处在于无法保证垃圾收集器何时会实际调用它,因此不建议依赖它。

否则,您可以做的最好的事情是try/finally阻止。

答案 1 :(得分:5)

Java没有析构函数。 但是有几种“标准”解决方案可以帮助您。

1.使用名为“模板方法”的模式。

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

现在你应该在你创建的每个子类中实现doQuery(),并通过从基类调用'query()'来使用它。

2.使用面向方面的编程

3.使用装饰器(包装)模式。

4.使用一个流行的ORM框架(Hibernate,iBatis等)为您解决所有这些问题,并且根本不处理低级JDBC的东西。

答案 2 :(得分:4)

想到几个解决方案......

正如@Dark Falcon指出的那样,这将是try with resources调用的一个很好的用例,它将在尝试结束时自动清理您的资源。不幸的是,这仅适用于Java 7。

Java类确实定义了一个finalize()方法,当对象被垃圾收集时可以调用它,但是覆盖这个方法几乎不是正确的做法。

我认为,如果你迷上“在函数返回时执行代码”的想法,你唯一的另一个选择就是使用Aspect Oriented Programming。如果您阅读了AspectJ等一些软件包或查看使用AOP with Spring,您可以执行一些配置魔术,以便在函数通过拦截调用返回时执行代码。这是使用Spring AOP在函数返回时执行其他代码的an example

答案 3 :(得分:2)

如果选择更新到java 7,则会有try with resourcesclose执行方法Closable

答案 4 :(得分:1)

我只有一种方法,只做一次。

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();
    }
}

update(connection, updateSQL1);
update(connection, updateSQL2);
// etc.

答案 5 :(得分:0)

我知道这是一个老问题,但为了别人的利益:

您可以使用实现接口的匿名内部类来完成工作,类似于Java Comparator和List排序机制的工作方式。这允许您将内部类视为执行范围。

e.g。修改原始示例:

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;
      }
   }
}

然后是一个如何使用它的例子:

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();
}

如果您使用的是Java 8,那么使用lambdas会更加漂亮,因为它会保存new AutoRollback语法。

如果您没有Java 8并且多余的标点符号仍然困扰您,您应该能够使用注释处理器和代码注入来使其读取漂亮。编译时注释设置为LOCAL_VARIABLE的目标是您想要的,然后将其应用于my_transaction

...假设注释处理器(如apt或预处理器)在您的工作场所是允许的,并且您希望为语法糖做那么多工作。