为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用
Posted
技术标签:
【中文标题】为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用【英文标题】:Why use @Transactional with @Service instead of with @Controller为什么将@Transactional 与@Service 一起使用而不是与@Controller 一起使用 【发布时间】:2013-09-01 03:06:34 【问题描述】:我在 stack-overflow 文章中看到了很多 cmets 我发现了一些关于 @Transactional 与 @Service 或 @Controller 一起使用的内容
“通常情况下,应该将事务放在服务层。”
“正常情况是在服务层级别进行注释”
“认为事务属于服务层。它了解工作单元和用例。如果您将多个 DAO 注入到需要在单个事务中协同工作的服务中,这是正确的答案。” [Source]
将@transactional 与@service 层一起使用的缺点
如果我有 2 种方法,例如 saveUser() 和 saveEmail()(因为我将电子邮件存储在数据库中以便稍后发送它们 - 就像一个队列)我会在我的服务中创建一个方法 saveUserAndSendEmail(User user) 这将交易。 [Source]
这意味着我在服务层创建了许多方法,而不是一个保存通用方法,如下所示
public <T> long save(T entity) throws DataAccessException
Session session = sessionFactory.getCurrentSession();
long getGenVal=(Long) session.save(entity);
return getGenVal;
根据上面的解决方案,这意味着我们有很多方法,如以下LOL..
public <T> long saveAccount(T entity)....
public <T> long saveWithAuditLog(T entity, K entity1)....
public <T> long saveWithAuditLogAndEntries(T entity, K entity, M entity)....
克服这种情况
我在@Controller 中使用@Transactional 并创建一个通用保存方法并使用这个简单的保存方法保存所有实体/模型。如果任何方法保存失败,则控制器中的所有事务都成功回滚。
确保@Transactional 应与@Controller 一起使用的其他情况
在@Controller 中:
pt.save(entity1);
pt.save(entity2);
int a = 2/0;
pt.save(entity3);
万一,@Transactional on Service,前 2 个实体成功保存,但第三个不是,它不会回滚所有事务
万一,@Controller 上的@Transactional 发生异常时所有事务回滚
为什么 stack-overflow 会问,“不要在你的控制器中做事务。把它们放在你的服务层类中。”? [source]
【问题讨论】:
我认为这种情况下最好的解决方案是在controller
和service
之间创建另一个级别层。正如我所看到的,controller
只处理调用并准备变量来处理它们并对其执行逻辑。 service
处理所有数据库内容。应该在controller
和service
之间的模型可以做一些逻辑。在您的示例中,执行少量保存操作是对 data 的逻辑操作。这意味着将它们放入model
并在需要时使用transactional
扭曲model
您的saveUser()
和saveEmail()
方法应该在DAO 层中。然后你让你在服务层事务中的saveUserAndSendEmail(User user)
方法。
【参考方案1】:
您在询问最佳实践,最佳实践是在服务层中标记 @Transactional
,因为 @Controller
不应该知道 MVC 逻辑中的数据持久性。@Service
是在使用时构造的- 从分析生成的案例并了解工作单元,并且在重用方面也实现了思考:如果您从 Web 上下文切换到桌面环境(例如,或其他一些可视化前端),@Controller
层没有存在您没有问题,因为所有内容都封装在服务层中。@Service
是一个合约,表示层的修改不需要重写 @Service
代码。
但是 Spring 不关心你把你的事务边界放在哪里,你可以放在 @Controller
但是你的应用程序可能会更难维护。
我希望这已经足够清楚了。抱歉,如果没有;英语不是我的母语。
【讨论】:
【参考方案2】:让其他人知道,不鼓励使用接口注释。
Spring 建议您仅使用 @Transactional
注释来注释具体类(和具体类的方法),而不是注释接口。您当然可以将@Transactional
注释放在接口(或接口方法)上,但这仅在您使用基于接口的代理时才有效。 Java 注释不是从接口继承的事实意味着,如果您使用基于类的代理 (proxy-target-class="true"
) 或基于编织的方面 (mode="aspectj"
),则代理和编织基础架构无法识别事务设置,并且该对象不会被包装在事务代理中,这绝对是不好的。
【讨论】:
【参考方案3】:Controller 牢牢地位于视图层,可以随时更改。该服务仍然拥有工作单元,并且无论访问哪个视图都应该正确运行。我的回答 here 仍然有效。
【讨论】:
这意味着我必须创建许多保存方法,例如saveAccount
saveAccountAudit
saveAccountAuditEntries
?【参考方案4】:
我创建了一个使用其他(非事务性)服务的上层服务。而且上层服务是事务性的。
@Service
public class Service1Impl implements Servcie1
public Object method11()...
public Object method12()...
@Service
public class Service2Impl implements Service2
public method21()...
public interface Service1
public Object method11();
public Object method12();
public interface Service2
public Object method21();
@Transactional
public interface UpperLayerService
@Service
public class UpperLayerServiceImpl implements UpperLayerService
@Autowired
private Service2 service2;
@Autowired
private Service3 service3;
public Object doWork()
Object o1 = service1.method11();
Object o2 = service1.method12();
Object o3 = service2.method21();
....
Object res = null;//...Any code
return res;
【讨论】:
以上是关于为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
为啥没有@Transactional 我可以保存? [复制]
尽管存在`@Transactional`,为啥这些数据库修改不回滚?
为啥使用 Spring Data JPA 更新实体时@Transactional 隔离级别不起作用?