对于 Web MVC Spring 应用程序,@Transactional 应该使用控制器还是服务? [英] For web MVC Spring app should @Transactional go on controller or service?

查看:17
本文介绍了对于 Web MVC Spring 应用程序,@Transactional 应该使用控制器还是服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于 WebApplicationContext,我应该将 @Transactional 注释放在控制器中还是放在服务中?Spring 文档让我有点困惑.

For WebApplicationContext, should I put @Transactional annotations in the controller or in services? The Spring docs have me a bit confused.

这是我的 web.xml:

Here is my 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>

这是我的 application-context.xml 定义了一个 spring 调度程序 servlet:

Here is my application-context.xml defining a spring dispatcher servlet:

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

Spring 文档让我有点困惑.这似乎表明使用 WebApplicationContext 意味着只会针对 @Transactional 注释而不是服务调查控制器.与此同时,我看到了大量的建议,使服务成为事务性的而不是控制器.我认为在上面的 spring-servlet.xml 中使用 <context:component-scan base-package="com..."/> 以便它包含服务包意味着服务是上下文的一部分,因此将针对事务注释进行调查".这准确吗?

The Spring documentation has me a bit confused. It seems to indicate that using a WebApplicationContext means only controllers will be investigated for @Transactional annotations and not services. Meanwhile I see tons of recommendations to make services transactional and not controllers. I'm thinking that using <context:component-scan base-package="com..." /> in our spring-servlet.xml above so that it includes the services packages means the services are part of the context, and therefore will be "investigated" for transactional annotations. Is this accurate?

这是让我感到困惑的 Spring 文档简介:

Here's the Spring documentation blurb that got me confused:

@EnableTransactionManagement 并且只看对于在同一应用程序上下文中的 bean 上的 @Transactional,它们是定义在.这意味着,如果你把注解驱动在 WebApplicationContext 中为 DispatcherServlet 配置,它只检查控制器中的 @Transactional bean,而不是你的服务.

@EnableTransactionManagement and only looks for @Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.

此外,如果我将控制器方法定义为事务性的,并且它在另一个类中调用事务性方法,是否有任何性能影响或坏处"?根据文档,我的预感是不,但希望对此进行验证.

Further, is there any performance implications or "badness" if I define a controller method as transactional, and it calls a transactional method in a another class? My hunch is no, based on the documentation, but would love validation on that.

推荐答案

@Transactional 注释应该放在 Controller 还是 Service 上没有要求,但通常会继续为一个请求执行逻辑的服务,逻辑上应该在一个 ACID 事务中执行.

There is no requirement for whether the @Transactional annotation should go on a Controller or on a Service, but typically it would go on a Service that would perform the logic for one request that logically should be performed within one ACID transaction.

在典型的 Spring MVC 应用程序中,您至少会拥有两个上下文:应用程序上下文和 servlet 上下文.上下文是一种配置.应用程序上下文保存与整个应用程序相关的配置,而 servlet 上下文保存仅与您的 servlet 相关的配置.因此,servlet 上下文是应用程序上下文的子级,可以引用应用程序上下文中的任何实体.反过来就不对了.

In a typical Spring MVC application, you would have, minimally, two contexts: the application context and the servlet context. A context is a sort of configuration. The application context holds the configuration that is relevant for your entire application, whereas the servlet context holds configuration relevant only to your servlets. As such, the servlet context is a child of the application context and can reference any entity in the application context. The reverse is not true.

在您的报价中,

@EnableTransactionManagement 并且只在定义它们的相同应用程序上下文中的 bean 上查找 @Transactional.这意味着,如果您将注解驱动的配置放在 DispatcherServlet 的 WebApplicationContext 中,它只会检查控制器中的 @Transactional bean,而不是您的服务.

@EnableTransactionManagement and only looks for @Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.

@EnableTransactionManagement@ComponentScan 注释中声明的包中的 bean 中查找 @Transactional,但仅在上下文 (@配置),它们是在其中定义的.因此,如果您的 DispatcherServlet(这是一个 servlet 上下文)有一个 WebApplicationContext,那么 @EnableTransactionManagement 将在您告诉它在该上下文中进行组件扫描的类(@Configuration 类)中查找 @Transactional.

@EnableTransactionManagement looks for @Transactional in beans in packages declared in the @ComponentScan annotation but only in the context (@Configuration) they are defined in. So If you have a WebApplicationContext for your DispatcherServlet (this is a servlet context), then @EnableTransactionManagement will look for @Transactional in classes you told it to component scan in that context (@Configuration class).

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

由于您的 @Service 类是应用程序上下文的一部分,如果您想使这些具有事务性,那么您需要为应用程序上下文注释您的 @Configuration 类使用 @EnableTransactionManagement.

Since your @Service classes are part of the Application context, if you want to make those transactional, then you need to annotate your @Configuration class for the Application Context with @EnableTransactionManagement.

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

在实例化 DispatcherServlet 时,使用带有 ContextLoaderListener 的应用程序上下文配置和 Servlet 上下文配置.(参见 javadoc 用于完整的基于 java 的配置,而不是 xml,如果您还没有这样做的话.)

Use your Application Context configuration with a ContextLoaderListener and your Servlet Context configuration when instantiating your DispatcherServlet. (See the javadoc for a full java based config, instead of xml, if you aren't doing it already.)

附录: @EnableTransactionManagement 与 java 配置中的 具有相同的行为.点击此处使用ContextLoaderListener 使用 XML.

Addendum: @EnableTransactionManagement has the same behavior as <tx:annotation-driven /> in a java configuration. Check here for using ContextLoaderListener with XML.

这篇关于对于 Web MVC Spring 应用程序,@Transactional 应该使用控制器还是服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆