【问题标题】:Where should we use @Transactional and where is Service layer?我们应该在哪里使用@Transactional,服务层在哪里?
【发布时间】:2014-10-21 16:31:16
【问题描述】:

我在 Spring 中有休息风格的控制器。在控制器中,我注入了 dao 接口。从控制器我持久化数据。换句话说,我喜欢 REST Web 服务。人们向我发送数据,我坚持下去。

/**
 * Payment rest controller which receives 
 * JSON of data
 */
@Controller
@RequestMapping("/data")
public class PaymentTransaction {

    @Autowired
    private TestDao dao;

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    @ResponseBody()
    public String test(HttpServletRequest request) {

    ...

    }

目前我在 Dao 类中有 @transaction 注解。例如:

    import org.springframework.transaction.annotation.Transactional;
    @Component
    @Transactional
    public interface TestDao {

        @Transactional(propagation = Propagation.REQUIRED)
        public void first();

    }

I have read that this is very bad style. Using this answer at stackoverflow ,这里是解释和示例为什么这是不好的 - 我们不能在 DAO 和控制器中添加这个注释。我们必须将它添加到服务层。

但是我不明白什么是服务层?或者它在哪里?我没有这样的东西。 我应该在哪里写@Transactional注解?

最好的问候,

【问题讨论】:

    标签: spring spring-mvc jpa


    【解决方案1】:

    根据cited 的帖子,您应该以某种方式设计您的类(相当伪代码):

    • controller(负责处理客户的请求/响应)

      @Controller
      @RequestMapping("/data")
      public class TestREST {
          @Autowired
          private TestService service;
      
          public void storePayment(PaymentDTO dto) { 
              service.storePayment(dto); //request from a client
          }
      
          public PaymentDTO getPayment(int paymentId) { 
              return service.getPayment(paymentId); //response to a client
          }
      }
      
    • 服务层(也叫业务层,负责业务逻辑——知道如何处理传入的消息,但不知道它们来自哪里)。

      public class TestServiceImpl {
          @Autowired
          private TestDao dao;
      
          @Transactional(propagation=Propagation.REQUIRED) //force transaction
          public void storePayment(PaymentDTO paymentDto) {
              // transform dto -> entity
              dao.storePayment(paymentEntity); //read-write hence transaction is on
          }
      
          @Transactional(propagation=Propagation.NOT_SUPPORTED) //avoid transaction
          public Payment getPayment(int paymentId) {
              return dao.findPayment(paymentId); //read-only hence no transaction
          }
      }
      
    • 数据访问层(也叫持久层,负责访问数据库——知道怎么用实体模型/ORM,对上层服务层一无所知)

      public class TestDAOImpl {
          @PersistenceContext
          private EntityManager em;
      
          public void storePayment(PaymentEntity paymentEntity) {
              em.persist(paymentEntity);
          }
      
          public PaymentEntity getPayment(int paymentId) {
              return em.find(PaymentEntity.class, paymentId);
          }
      }
      

    通过这种方法,您会得到帖子中提到的关注点分离。另一方面,这种方法(业务层与数据访问层)在 Adam Bien 的blog(“JPA/EJB3 杀死了 DAO”)中受到了一点批评。如您所见,该问题没有单一的解决方案,但我鼓励您阅读其他意见并应用您认为最适合您需求的解决方案。

    【讨论】:

    • 你注入了这样的测试服务:TestService服务;但是测试服务类有名称“TestServiceImpl”这是不正确的不是吗?我们需要在这里实现接口TestService吗?还是我们应该写 TestService 而不是 TestServiceImpl?
    • 正确。我刚刚跳过了它们。所有 *Impl 类都应该有接口TestServiceImpl implements TestService 等等。因此,您可以注入任何实现给定接口的类 - 很有用,即用于测试目的,您希望在给定层的不同实现之间切换。
    【解决方案2】:

    当您从控制器调用两个 Dao 方法 first 和 second 时,将完成 2 个事务,一个在第一个方法之前开始并在其执行后结束,第二个在第二个方法开始之前开始并在其执行后结束。虽然您在控制器和 dao 之间创建了一个附加类(通常称为服务层)并使用 @Transactional 对其进行注释并在其中调用多个 Dao 方法,但事务在服务方法开始时启动,所有 dao 调用将是执行并且交易将被关闭,这是您所需要的。并将 Service 注入 Controller。

    控制器 -> 服务 -> 道

    @Controller
    @RequestMapping("/data")
    public class PaymentTransaction {
    
        @Autowired
        private TestService service;
    
        @RequestMapping(value = "/test", method = RequestMethod.POST)
        @ResponseBody()
        public String test(HttpServletRequest request) {
    
          ...
    
        }
    }
    
    @Service
    @Transactional
    public class TestService {
    
        @Autowired
        private TestDao dao;
    
        @Transactional
        public void serviceCall(){
             dao.first();
             dao.second();
        }
    
    }
    

    【讨论】:

    • 我不明白,如果我的控制器是上层而不是服务,为什么我需要中间?我可以从控制器控制一切不是吗? ps如何从控制器访问服务层?我应该使用任何注释还是...
    • 您必须将服务注入控制器,请参阅编辑后的 ​​anwser。如果你真的想要,你也可以在控制器中使用@Transactional,但这被认为是一种不好的做法。
    • 这叫做关注点分离。服务层是您的业务逻辑的门面,也是事务发生的地方(正如@Janedra 所指出的那样),有时 bean 映射到数据传输对象,而存储库访问发生在数据访问层(您的 DAO)中。控制器应该只负责根据用户(或任何外部代理)操作“决定”做什么,并且应该针对接口(您的服务层)工作,甚至不知道“幕后”发生了什么。
    猜你喜欢
    • 2011-04-22
    • 2017-01-06
    • 1970-01-01
    • 2015-01-07
    • 2017-11-06
    • 2012-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多