为什么我们不应该使用Spring MVC控制器@Transactional? [英] Why we shouldn't make a Spring MVC controller @Transactional?

查看:168
本文介绍了为什么我们不应该使用Spring MVC控制器@Transactional?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

已经有一些关于主题的问题,但没有任何响应真的提供参数,以解释为什么我们不应该创建一个Spring MVC控制器 Transactional 。请参阅:





那么,为什么?




  • 是否存在不可逾越的技术问题?

  • 是否存在架构问题?

  • 是否存在性能/死锁/并发问题?

  • 需要单独交易?如果是,用例是什么? (我喜欢简单的设计,调用服务器完全成功或完全失败。这听起来是一个非常稳定的行为)



背景:
我在几年前在一个团队工作在一个相当大的ERP软件实现在C#/ NHibernate / Spring.Net。到服务器的往返是如此完成的:事务在进入任何控制器逻辑之前被打开,并且在退出控制器之后被提交或回滚。该事务在框架中进行管理,因此没有人关心它。这是一个非常出色的解决方案:稳定,简单,只有少数建筑师不得不关心交易问题,其他团队只是实现了功能。



从我的角度来看,这是我见过的最好的设计。当我试图重现与Spring MVC相同的设计,我进入一个噩梦与延迟加载和事务问题,每一次相同的答案:不使控制器事务,但为什么?


解决方案

TLDR :这是因为只有应用程序中的服务层具有识别数据库/业务事务范围所需的逻辑。控制器和持久层通过设计不能/不应该知道事务的范围。



控制器可以做成 @Transactional ,但实际上,这是一个常见的建议,只使服务层事务性(持久层不应该是事务性的)。



不是技术可行性,而是分离的关注点。控制器的职责是获取参数请求,然后调用一个或多个服务方法,并将结果合并到响应中,然后发送回客户端。



控制器具有请求执行的协调器和域数据的变换器到客户端可以消费的格式(例如DTO)的功能。



业务逻辑驻留在服务层,持久层只是从数据库中来回检索/存储数据。



数据库事务的范围实际上是一个业务概念以及一个技术概念:在帐户转移中,只有在其他帐户被贷记等情况下,才能从帐户中扣除,因此只有包含业务的服务层逻辑可以真正知道银行帐户转帐交易的范围。



持久层不能知道它是在什么交易,例如一个方法 customerDao .saveAddress 。它应该在它自己独立的事务总是运行吗?有没有办法知道,它取决于业务逻辑调用它。有时它应该运行在单独的事务上,有时只是保存它的数据如果 saveCustomer 也工作等。



这同样适用于控制器:应该 saveCustomer saveErrorMessages 进入同一个事务吗?您可能希望保存客户,如果失败,请尝试保存一些错误消息,并向客户端返回正确的错误消息,而不是回滚所有包括您想要保存在数据库上的错误消息。



在非事务控制器中,从服务层返回的方法返回分离的实体,因为会话已关闭。这是正常的,解决方案是使用 OpenSessionInView 或者做查询,渴望获取控制器知道它需要的结果。



话虽如此,使控制器交易并不是一种犯罪,但它不是最常用的做法。


There are already a few questions about the topic, but no response at all really provides arguments in order to explain why we shouldn't make a Spring MVC controller Transactional. See:

So, why?

  • Is there insurmountable technical issues?
  • Is there architectural issues?
  • Is there performance/deadlock/concurrency issues?
  • Are sometimes multiple separate transactions required? If yes, what are the use cases? (I like the simplifying design, that calls to server either completely succeed or completely fail. It sounds to be a very stable behavior)

Background: I worked a few years ago in a Team on a quite large ERP Software implemented in C#/NHibernate/Spring.Net. The round-trip to the server was exactly implemented like that: the transaction was opened before entering any controller logic and was committed or rollbacked after exiting the controller. The transaction was managed in the framework so that no one had to care about it. It was a brilliant solution: stable, simple, only a few architects had to care about transaction issues, the rest of the team just implemented features.

From my point of view, it is the best design I have ever seen. As I tried to reproduce the same design with Spring MVC, I entered in a nightmare with lazy-loading and transaction issues and every time the same answer: don't make the controller transactional, but why?

Thank you in advance for your founded answers!

解决方案

TLDR: this is because only the service layer in the application has the logic needed to identify the scope of a database/business transaction. The controller and persistence layer by design can't/shouldn't know the scope of a transaction.

The controller can be made @Transactional, but indeed it's a common recommendation to only make the service layer transactional (the persistence layer should not be transactional either).

The reason for this is not technical feasibility, but separation of concerns. The controller responsibility is to get the parameter requests, and then call one or more service methods and combine the results in a response that is then sent back to the client.

So the controller has a function of coordinator of the request execution, and transformer of the domain data to a format the client can consume such as DTOs.

The business logic resides on the service layer, and the persistence layer just retrieve / stores data back and forth from the database.

The scope of a database transaction is really a business concept as much as a technical concept: in an account transfer an account can only be debited if the other is credited etc., so only the service layer that contains the business logic can really know the scope of a bank account transfer transaction.

The persistence layer cannot know what transaction it's in, take for example a method customerDao.saveAddress. Should it run in it's own separate transaction always? there is no way to know, it depends on the business logic calling it. Sometimes it should run on a separate transaction, sometimes only save it's data if the saveCustomer also worked, etc.

The same applies to the controller: should saveCustomer and saveErrorMessages go in the same transaction? You might want to save the customer and if that fails then try to save some error messages and return a proper error message to the client, instead of rolling back everything including the error messages you wanted to save on the database.

In non transactional controllers, methods returning from the service layer return detached entities because the session is closed. This is normal, the solution is to either use OpenSessionInViewor do queries that eager fetch the results the controller knows it needs.

Having said that, it's not a crime to make controllers transactional, it's just not the most frequently used practice.

这篇关于为什么我们不应该使用Spring MVC控制器@Transactional?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆