【问题标题】:"Transaction in view" with Hibernate, Spring, Struts使用 Hibernate、Spring、Struts 的“视图中的事务”
【发布时间】:2011-06-13 04:22:04
【问题描述】:
来自 Hibernate 参考手册:
"使用单个数据库事务处理客户端请求,在您打开和关闭 Session 时启动并提交它"
Spring 支持这种模式吗?
我一直在使用带有@Transactional 注释和“在视图中打开会话”模式(org.springframework.orm.hibernate3.support.OpenSessionInViewFilter)的 Spring 事务支持,但事务必须限于服务方法,所以我得到了多个事务每个视图,而不仅仅是一个。
【问题讨论】:
标签:
hibernate
spring
transactions
struts
transactionscope
【解决方案1】:
您可能不想要“视图中的事务”类型的功能。通常,请求发生的情况是
- 请求进来
- 调用一些将内容保存到数据库的操作
- 响应消失。即呈现成功页面或返回 json 或其他任何内容
你不想要'transaction in view'的原因是因为如果在第3步出现错误,你的事务将被回滚,即使实际的业务逻辑是成功的,只是渲染响应失败。
现在,我假设您希望即使在提交数据后出现错误也能提交数据。如果这个假设是错误的,那么好吧,这取决于你。
由于服务方法包含在事务中,可能更简单的解决方案是重构代码,以便每个传入请求调用一个服务。通常这被称为“立面模式”。假设你有
Service1.op1()
Service2.op2()
并且您为一个请求调用这两种方法。您可以简单地创建另一个服务
AppFacade.doOp1andOp2()
在相关服务上调用 op1 和 op2。
另一种可能性是配置 Spring 以使用声明式事务管理将事务放在您的 Struts Actions 周围。请注意事务,从 tx 打开到关闭,所有 db 操作都将使用同一个事务,因此即使您调用多个服务,它们都使用相同的 tx.=。请参阅此处的文档:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
特别是 10.5.2
但我认为每个业务功能单元使用一个服务调用是最好的方法。
【解决方案2】:
当您使用“在视图中打开会话”模式 (org.springframework.orm.hibernate3.support.OpenSessionInViewFilter) 以在您的 http 呈现(例如 JSP)中启用例如延迟加载,并将其与您的服务方法中 @Transactional 的事务控制混合使用时,那么这在我看来是一种最佳实践方法。
我这么认为的原因是:
- 如果您需要在 JSP 中进行延迟加载,那么“Open session in view”是必须的。
- 事务控制属于您的业务逻辑,因此不能(实际上)由过滤器完成。
所以至少你需要两者。
【解决方案3】:
声明一个实现 Hibernate 的 OpenSessionInViewFilter/Interceptor 的类.....
下面给出一个例子...
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.support.OpenSessionInViewFilter;
public class CustomHibernateSessionViewFilter extends OpenSessionInViewFilter {
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = super.getSession(sessionFactory);
session.setFlushMode(FlushMode.COMMIT);
return session;
}
protected void closeSession(Session session, SessionFactory factory) {
session.flush();
super.closeSession(session, factory);
}
}
在 web.xml(或应用程序上下文)中:-
<filter>
<filter-name>OSIVF Filter</filter-name>
<filter-class>your.path.to.CustomHibernateSessionViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OSIVF Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
希望这能解决您的问题。请让我知道它是否有效。