복붙노트

[SPRING] 웹 MVC Spring 애플리케이션의 경우 @Transactional이 컨트롤러 또는 서비스를 사용해야합니까?

SPRING

웹 MVC Spring 애플리케이션의 경우 @Transactional이 컨트롤러 또는 서비스를 사용해야합니까?

WebApplicationContext의 경우 @Transactional 주석을 컨트롤러 또는 서비스에 넣어야합니까? Spring 문서는 약간 혼란 스럽습니다.

다음은 web.xml입니다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Alpha v0.02</display-name>
  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.json</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

다음은 Spring Dispatcher 서블릿을 정의하는 application-context.xml입니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/beans    
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:annotation-config />
    <mvc:annotation-driven />
    <tx:annotation-driven />

    <context:component-scan base-package="com.visitrend" />

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

     <bean id="dataSource"  class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/postgres" />
        <property name="user" value="someuser" />
        <property name="password" value="somepasswd" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:test.hibernate.cfg.xml" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
      <property name="dataSource" ref="dataSource" />
      <property name="sessionFactory" ref="sessionFactory" />
    </bean>    
</beans>

다음은 서비스 인터페이스입니다.

public interface LayerService {
    public void createLayer(Integer layerListID, Layer layer);
}

다음은 서비스 구현입니다.

@Service
public class LayerServiceImpl implements LayerService {

    @Autowired
    public LayerDAO layerDAO;

    @Transactional
    @Override
    public void createLayer(Integer layerListID, Layer layer) {
        layerDAO.createLayer(layerListID, layer);
    }
}

그리고 여기 내 컨트롤러가 :

@Controller
public class MainController {

    @Autowired
    private LayerService layerService;

    @RequestMapping(value = "/addLayer.json", method = RequestMethod.POST)
    public @ResponseBody
    LayerListSetGroup addLayer(@RequestBody JSONLayerFactory request) {
        layerService.createLayer(request.getListId(), request.buildLayer());
        return layerService.readLayerListSetGroup(llsgID);
    }
}

스프링 문서는 다소 혼란 스럽다. WebApplicationContext를 사용하면 컨트롤러 만이 서비스가 아닌 @Transactional 주석에 대해 조사된다는 의미입니다. 한편 서비스를 트랜잭션이 아닌 컨트롤러로 만들 수있는 많은 추천을 봅니다. 위의 spring-servlet.xml에서 을 사용하여 서비스 패키지가 포함되어 서비스가 컨텍스트의 일부라는 것을 의미하므로 생각하고 있습니다. 트랜잭션 주석에 대해 "조사"됩니다. 정확합니까?

나에게 혼란스러워하는 스프링 문서를 보자.

또한 컨트롤러 메서드를 트랜잭션으로 정의하고 다른 클래스의 트랜잭션 메서드를 호출하면 성능에 영향을 미치거나 "나쁜 점"이 있습니까? 내 직감은 문서를 기반으로하는 것이 아니라 사실에 대한 검증을 좋아할 것입니다.

해결법

  1. ==============================

    1.@Transactional 어노테이션이 컨트롤러 또는 서비스에 있어야하는지에 대한 요구 사항은 없지만 일반적으로 하나의 ACID 트랜잭션 내에서 논리적으로 수행되어야하는 하나의 요청에 대한 로직을 수행하는 서비스로 이동합니다.

    @Transactional 어노테이션이 컨트롤러 또는 서비스에 있어야하는지에 대한 요구 사항은 없지만 일반적으로 하나의 ACID 트랜잭션 내에서 논리적으로 수행되어야하는 하나의 요청에 대한 로직을 수행하는 서비스로 이동합니다.

    일반적인 Spring MVC 애플리케이션에서는 최소한 애플리케이션 컨텍스트와 서블릿 컨텍스트의 두 가지 컨텍스트가 필요합니다. 컨텍스트는 일종의 구성입니다. 응용 프로그램 컨텍스트는 전체 응용 프로그램과 관련된 구성을 보유하는 반면 서블릿 컨텍스트는 서블릿에만 관련된 구성을 보유합니다. 따라서 서블릿 컨텍스트는 애플리케이션 컨텍스트의 하위이며 애플리케이션 컨텍스트의 모든 엔티티를 참조 할 수 있습니다. 그 반대는 사실이 아닙니다.

    귀하의 견적에서,

    @EnableTransactionManagement는 @ComponentScan 주석에서 선언 된 패키지의 Bean에서 @Transactional을 찾지만 정의 된 컨텍스트 (@Configuration)에서만 찾습니다. 따라서 DispatcherServlet에 대한 WebApplicationContext (서블릿 컨텍스트)가 있으면 @EnableTransactionManagement 해당 컨텍스트 (@Configuration 클래스)에서 구성 요소 검색에 대해 말한 클래스에서 @Transactional을 찾습니다.

    @Configuration
    @EnableTransactionManagement
    @ComponentScan(basePackages = "my.servlet.package")
    public class ServletContextConfiguration {
        // this will only find @Transactional annotations on classes in my.servlet.package package
    }
    

    @Service 클래스는 Application 컨텍스트의 일부이므로 이러한 트랜잭션을 만들려면 @EnableTransactionManagement를 사용하여 Application Context의 @Configuration 클래스에 주석을 추가해야합니다.

    @Configuration
    @EnableTransactionManagement
    @ComponentScan(basePackages = "my.package.services")
    public class ApplicationContextConfiguration {
        // now this will scan your my.package.services package for @Transactional
    }
    

    DispatcherServlet을 인스턴스화 할 때 ContextLoaderListener 및 Servlet 컨텍스트 구성과 함께 Application Context 구성을 사용하십시오. (xml이 아닌 이미 완전한 자바 기반 설정을 보려면 javadoc을 참조하십시오.

    부록 : @EnableTransactionManagement는 자바 설정에서 와 같은 동작을한다. ContextLoaderListener를 XML과 함께 사용하려면 여기를 확인하십시오.

  2. ==============================

    2.서비스는 거래 경계 설정에 가장 적합한 장소입니다. 이 서비스는 사용자 상호 작용에 대한 세부 수준의 유스 케이스 동작을 유지해야합니다. 즉, 논리적으로 트랜잭션에서 함께 수행되는 내용을 의미합니다. 또한 웹 애플리케이션 글루 코드와 비즈니스 로직간에 분리가 유지됩니다.

    서비스는 거래 경계 설정에 가장 적합한 장소입니다. 이 서비스는 사용자 상호 작용에 대한 세부 수준의 유스 케이스 동작을 유지해야합니다. 즉, 논리적으로 트랜잭션에서 함께 수행되는 내용을 의미합니다. 또한 웹 애플리케이션 글루 코드와 비즈니스 로직간에 분리가 유지됩니다.

    중요한 비즈니스 로직이없는 많은 CRUD 애플리케이션이 있습니다. 컨트롤러와 데이터 액세스 객체 사이를 통과하는 서비스 레이어가 유용하지 않기 때문입니다. 이 경우 트랜잭션 주석을 데이터 액세스 객체에 넣을 수 있습니다.

    트랜잭션 주석을 컨트롤러에두면 문제가 발생할 수 있습니다. [Spring MVC 문서] [1], 17.3.2 :

    특성에 설정 한 트랜잭션 전파 비헤이비어는 트랜잭션 방식이 다른 트랜잭션 방식을 호출 할 때 수행 할 작업을 결정합니다. 호출 된 메소드가 동일한 트랜잭션을 사용하거나 항상 새로운 트랜잭션을 사용하도록 메소드를 구성 할 수 있습니다.

    예제 코드에서 서비스 호출을 여러 번하면 서비스의 트랜잭션 목적을 상실하게됩니다. 서비스에 대한 다른 호출은 서비스에 트랜잭션 어노테이션을 넣으면 다른 트랜잭션에서 실행됩니다.

  3. ==============================

    3.때로는 Hibernate를 사용하여 사소한 연산을 수행 할 때 @Transactional 컨트롤러 메소드를 갖는 것이 매우 편리하다. XML 설정을 사용하려면 이것을 dispatch-servlet.xml에 추가하십시오 :

    때로는 Hibernate를 사용하여 사소한 연산을 수행 할 때 @Transactional 컨트롤러 메소드를 갖는 것이 매우 편리하다. XML 설정을 사용하려면 이것을 dispatch-servlet.xml에 추가하십시오 :

    <beans ...
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="...
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">    
      <tx:annotation-driven transaction-manager="transactionManager"
       proxy-target-class="true" />
      ..
    </beans>
    

    proxy-target-class의 목적은 컨트롤러에서 AOP가 작동하는 데 필요한 CGLIB 프록시를 사용하는 것입니다. 추가하지 않으면 시작시 오류가 발생합니다. 또한 컨트롤러에 최종 메소드가있는 경우, 프록시를 사용할 수 없으며 (특히 트랜잭션으로 처리 할 수 ​​없음), 시작할 때 이러한 각 메소드에 대해 CGLIB로부터 경고를받습니다.

  4. from https://stackoverflow.com/questions/15620355/for-web-mvc-spring-app-should-transactional-go-on-controller-or-service by cc-by-sa and MIT license