Where should we use @Transactional and where is Se

2020-06-27 07:46发布

I have rest style controller in Spring. In controller I have injected dao interfaces. From controller I persist data. In the other words, I have like REST web service. people sends me data, and I persits it.

/**
 * 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) {

    ...

    }

At the moment I have @transaction annotation in Dao classes. For instance:

    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 , here is explain and examples why is this bad - we must not add this annotation in DAO and in controller too. We must add it in service layer.

But I don't understand what is the service layer? Or where is it? I do not have anything like this. where should I write @Transactional annotation?

Best regards,

2条回答
三岁会撩人
2楼-- · 2020-06-27 08:40

When you call two Dao methods first & second from controller, 2 transactions will be done, one with starts before first method and ends after it's execution and the second one starts before second method starts and ends after it's execution. Whereas you create an additional class in between controller and dao (usually this is called service layer) and annotate it with @Transactional and call multiple Dao methods in it, a transaction is started at the start of service method and all the dao calls will be executed and transaction will be closed, which is what you require. And inject the Service into Controller.

Controller -> Service -> Dao

@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();
    }

}
查看更多
仙女界的扛把子
3楼-- · 2020-06-27 08:41

According to the cited post, you should design your classes somehow like this (rather pseudocode):

  • controller (responsible for handling clients' requests/responses)

    @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
        }
    }
    
  • service layer (also called business layer, responsible for business logic - knows what to do with incoming messages, but does not know where they come from).

    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
        }
    }
    
  • data access layer (also called persistence layer, responsible for accessing database - knows how to use entity model / ORM, does not know anything about the upper service layer)

    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);
        }
    }
    

By this approach you get separation of concerns mentioned in the post. From the other hand such an approach (business layer vs data access layer) got a little dose of criticism from Adam Bien's on his blog ("JPA/EJB3 killed the DAO"). As you can see there is no a single solution for the problem, but I encourage to read some other opinions and apply the solution you find the most suitable for your needs.

查看更多
登录 后发表回答