繁体   English   中英

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

[英]Java: call a function automatically at scope exit (like c++ destructors)

对于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块?

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

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

否则,你可以做的最好的是try/finally块。

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

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

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

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

2.使用面向方面的编程

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

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

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

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

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

我认为,如果你迷上“在函数返回时执行代码”的想法,那么你唯一的另一个选择就是使用面向方面编程 如果您阅读了一些像AspectJ这样的软件包或者考虑在Spring中使用AOP ,那么您可以执行一些配置魔术,以便在函数通过拦截调用返回时执行代码。 这是一个使用Spring AOP在函数返回时执行其他代码的示例

如果对java 7的更新是一个选项,则有一个新的try with resources将在Closable实现中执行close方法的try with resources

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

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.

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

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

例如,修改原始示例:

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或预处理器)在您的工作场所是允许的,并且您希望为语法糖做那么多工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM