[英]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.