【问题标题】: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)方法。

标签: hibernate spring-mvc service controller transactional


【解决方案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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-31
        • 2011-10-04
        • 1970-01-01
        相关资源
        最近更新 更多