为啥将@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 &lt;T&gt; long saveAccount(T entity)....

public &lt;T&gt; long saveWithAuditLog(T entity, K entity1)....

public &lt;T&gt; 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]

【问题讨论】:

我认为这种情况下最好的解决方案是在controllerservice 之间创建另一个级别层。正如我所看到的,controller 只处理调用并准备变量来处理它们并对其执行逻辑。 service 处理所有数据库内容。应该在controllerservice 之间的模型可以做一些逻辑。在您的示例中,执行少量保存操作是对 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`,为啥这些数据库修改不回滚?

为啥@Transactional 会自动保存到数据库

为啥使用 Spring Data JPA 更新实体时@Transactional 隔离级别不起作用?

Spring 声明式事务@Transactional不起作用,为啥

如何将 @Transactional 与 Spring Data 一起使用?