17

WebApplicationContext の場合、@Transactional注釈をコントローラーまたはサービスに配置する必要がありますか? 春のドキュメントは私を少し混乱させます。

ここに私の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);
    }
}

Spring のドキュメントでは、少し混乱しています。WebApplicationContext を使用すると、サービスではなく @Transactional アノテーションについてコントローラーのみが調査されることを意味するようです。一方、コントローラーではなく、サービスをトランザクション対応にするための推奨事項がたくさんあります。上記の spring-servlet.xmlを使用 <context:component-scan base-package="com..." />してサービス パッケージを含めることは、サービスがコンテキストの一部であることを意味するため、トランザクション アノテーションについて「調査」されると考えています。これは正確ですか?

私を混乱させたSpringドキュメントの宣伝文句は次のとおりです。

@EnableTransactionManagement を使用し、Bean が定義されているのと同じアプリケーション コンテキスト内の Bean で @Transactional のみを検索します。あなたのサービス。

さらに、コントローラーメソッドをトランザクションとして定義し、別のクラスでトランザクションメソッドを呼び出すと、パフォーマンスへの影響や「悪さ」はありますか? ドキュメントに基づいて、私の予感はノーですが、それについての検証が大好きです。

4

3 に答える 3

18

アノテーションをコントローラーまたはサービスのどちらに配置する必要があるかについての要件はありません@Transactionalが、通常は、1 つの ACID トランザクション内で論理的に実行する必要がある 1 つの要求のロジックを実行するサービスに配置します。

典型的な Spring MVC アプリケーションでは、最小限、アプリケーション コンテキストとサーブレット コンテキストの 2 つのコンテキストがあります。コンテキストは一種の設定です。アプリケーション コンテキストはアプリケーション全体に関連する構成を保持しますが、サーブレット コンテキストはサーブレットのみに関連する構成を保持します。したがって、サーブレット コンテキストはアプリケーション コンテキストの子であり、アプリケーション コンテキスト内の任意のエンティティを参照できます。逆は当てはまりません。

あなたの引用では、

@EnableTransactionManagement を使用し、Bean が定義されているのと同じアプリケーション コンテキスト内の Bean で @Transactional のみを検索します。これは、DispatcherServlet の WebApplicationContext にアノテーション駆動型の構成を配置した場合、コントローラー内の @Transactional Bean のみをチェックし、チェックしないことを意味します。あなたのサービス。

@EnableTransactionManagementアノテーション@Transactionalで宣言されたパッケージ内の Bean を検索しますが、それらが定義されているコンテキスト ( )内のみです。そのコンテキスト(クラス)。@ComponentScan@ConfigurationWebApplicationContextDispatcherServlet@EnableTransactionManagement@Transactional@Configuration

@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

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

DispatcherServlet をインスタンス化するときに、Application Context 構成とContextLoaderListenerServlet Context 構成を使用します。(まだ行っていない場合は、xml ではなく完全な Java ベースの構成については javadoc を参照してください。)

補遺: Java 構成@EnableTransactionManagementと同じ動作をします。XMLでの利用はこちらをご確認ください。<tx:annotation-driven />ContextLoaderListener

于 2013-03-25T20:41:24.997 に答える
5

サービスは、トランザクションの境界を設定するのに最適な場所です。サービスは、ユーザー インタラクションの詳細レベルのユース ケース動作を保持する必要があります。つまり、トランザクションで論理的に一緒になるものを意味します。また、そのようにして、Web アプリケーションのグルー コードとビジネス ロジックの間の分離が維持されます。

重要なビジネス ロジックを持たない多くの CRUD アプリケーションがあります。コントローラーとデータ アクセス オブジェクトの間を通過するだけのサービス レイヤーは役に立たないからです。そのような場合は、トランザクション アノテーションをデータ アクセス オブジェクトに配置することで回避できます。

コントローラーにトランザクション アノテーションを配置すると、問題が発生する可能性があります。[Spring MVC のドキュメント][1]、17.3.2 を参照してください。

アノテーション付きコントローラー クラスを操作する際のよくある落とし穴は、コントローラー オブジェクトのプロキシを作成する必要がある機能 (@Transactional メソッドなど) を適用するときに発生します。通常、JDK 動的プロキシーを使用するために、コントローラー用のインターフェースを導入します。これを機能させるには、@RequestMapping アノテーション、およびその他の型およびメソッド レベルのアノテーション (@ModelAttribute、@InitBinder など) をインターフェースに移動する必要があります。また、マッピング メカニズムは、プロキシー。または、コントローラーに適用される機能の構成で proxy-target-class="true" を有効にすることもできます ( のトランザクション シナリオ)。これは、インターフェイスベースの JDK プロキシの代わりに、CGLIB ベースのサブクラス プロキシを使用する必要があることを示しています。

属性に設定したトランザクション伝搬動作によって、トランザクション メソッドが別のトランザクション メソッドを呼び出したときに何が起こるかが決まります。呼び出されたメソッドが同じトランザクションを使用するように、または常に新しいトランザクションを使用するように構成できます。

サンプル コードでサービスを複数回呼び出すと、サービスのトランザクション目的が無効になります。サービスにトランザクション アノテーションを配置すると、サービスへのさまざまな呼び出しがさまざまなトランザクションで実行されます。

于 2013-03-25T19:42:58.480 に答える
0

特に 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 プロキシーを使用することです。これを追加しないと、起動時にエラーが発生します。また、コントローラーに final メソッドがある場合は、それらをプロキシできない (特に、トランザクション化する) ことができないことに注意してください。また、起動時にこれらのメソッドごとに CGLIB から警告が表示されます。

于 2015-04-26T06:22:05.753 に答える