如何在不丢失封装的情况下进行事务处理?
我有一个代码可以保存一个 bean,并通过 Hibernate 更新数据库中的另一个 bean。它必须在同一个事务中执行,因为如果发生错误(f.ex 启动异常),两个操作必须执行回滚。
public class BeanDao extends ManagedSession {
public Integer save(Bean bean) {
Session session = null;
try {
session = createNewSessionAndTransaction();
Integer idValoracio = (Integer) session.save(bean); // SAVE
doOtherAction(bean); // UPDATE
commitTransaction(session);
return idBean;
} catch (RuntimeException re) {
log.error("get failed", re);
if (session != null) {
rollbackTransaction(session);
}
throw re;
}
}
private void doOtherAction(Bean bean) {
Integer idOtherBean = bean.getIdOtherBean();
OtherBeanDao otherBeanDao = new OtherBeanDao();
OtherBean otherBean = otherBeanDao.findById(idOtherBean);
.
. (doing operations)
.
otherBeanDao.attachDirty(otherBean)
}
}
问题是:
如果
session.save(bean)
启动错误,那么我会得到 AssertionFailure,因为函数 doOtherAction (在项目的其他部分中使用)在抛出异常后使用会话。
我首先想到的是提取函数 doOtherAction 的代码,但后来我有相同的代码重复,这似乎不是最好的做法。
重构这个的最好方法是什么?
I have a code that saves a bean, and updates another bean in a DB via Hibernate. It must be do in the same transaction, because if something wrong occurs (f.ex launches a Exception) rollback must be executed for the two operations.
public class BeanDao extends ManagedSession {
public Integer save(Bean bean) {
Session session = null;
try {
session = createNewSessionAndTransaction();
Integer idValoracio = (Integer) session.save(bean); // SAVE
doOtherAction(bean); // UPDATE
commitTransaction(session);
return idBean;
} catch (RuntimeException re) {
log.error("get failed", re);
if (session != null) {
rollbackTransaction(session);
}
throw re;
}
}
private void doOtherAction(Bean bean) {
Integer idOtherBean = bean.getIdOtherBean();
OtherBeanDao otherBeanDao = new OtherBeanDao();
OtherBean otherBean = otherBeanDao.findById(idOtherBean);
.
. (doing operations)
.
otherBeanDao.attachDirty(otherBean)
}
}
The problem is:
In case that
session.save(bean)
launches an error, then I get AssertionFailure, because the function doOtherAction (that is used in other parts of the project) uses session after a Exception is thrown.
The first thing I thought were extract the code of the function doOtherAction, but then I have the same code duplicate, and not seems the best practice to do it.
What is the best way to refactor this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在服务或其他业务逻辑类中,在 DAO 之上的一级管理事务是一种常见的做法。这样,您就可以根据业务/服务逻辑,在一种情况下在一个事务中执行两个 DAO 操作,在另一种情况下,在单独的事务中执行这些操作。
It's a common practice to manage transactions at one level above DAOs, in services or other business logic classes. That way you can, based on the business/service logic, in one case do two DAO operations in one transaction and, in another case, do them in separate transactions.
我是声明式事务管理的忠实粉丝。如果您能抽出时间让它工作(使用 应用程序小菜一碟服务器,例如 GlassFish 或 JBoss,并且可以轻松使用 春天)。如果您使用 @TransactionAttribute(REQUIRED) 注释您的业务方法(甚至可以将其设置为默认完成),并且它调用两个 DAO 方法,您将得到您想要的一切:一切都在一次或因异常而回滚。
该解决方案是尽可能松散耦合的。
I'm a huge fan of Declarative Transaction Management. If you can spare the time to get it working (piece of cake with an Application Server such as GlassFish or JBoss, and easy with Spring). If you annotate your business method with
@TransactionAttribute(REQUIRED)
(it can even be set to be done as default) and it calls the two DAO methods you will get exactly what you want: everything gets committed at once or rolled back over an Exception.This solution is about as loosely coupled as it gets.
其他人是正确的,因为他们考虑了当前的常见做法。
但这对你目前的练习并没有真正的帮助。
您应该做的是创建两个新的 DAO 方法。例如CreateGlobalSession和CommitGlobalSession。
它们的作用与您当前的创建和提交例程相同。
不同之处在于它们设置了一个“全局”会话变量(很可能最好使用 ThreadLocal 来完成)。然后,您更改当前例程,以便它们检查此全局会话是否已存在。如果您的创建检测到全局会话,则只需返回它即可。如果您的提交检测到全局会话,那么它什么也不做。
现在,当您想要使用它时,您可以这样做:
确保将进程包装在 try 块中,以便在出现错误时可以重置全局会话。
虽然其他技术被认为是最佳实践,并且理想情况下您有一天可以发展到类似的技术,但这将帮助您克服困难,只需要 3 种以上的新方法并更改两种现有方法。之后,其余代码保持不变。
The others are correct in that they take in to account what are common practice currently.
But that doesn't really help you with your current practice.
What you should do is create two new DAO methods. Such as CreateGlobalSession and CommitGlobalSession.
What these do is the same thing as your current create and commit routines.
The difference is that they set a "global" session variable (most likely best done with a ThreadLocal). Then you change the current routines so that they check if this global session already exists. If your create detects the global session, then simply return it. If your commit detects the global session, then it does nothing.
Now when you want to use it you do this:
Make sure you wrap the process in a try block so that you can reset your global session if there's an error.
While the other techniques are considered best practice and ideally you could one day evolve to something like that, this will get you over the hump with little more than 3 new methods and changing two existing methods. After that the rest of your code stays the same.