对于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块?
在范围退出时自动调用函数的方法会对我有所帮助。
答案 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 resources
个close
执行方法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或预处理器)在您的工作场所是允许的,并且您希望为语法糖做那么多工作。