2012-09-07 20 views
10

私の問題の解決策を見つけるのに苦労しています。
私はログイン時に確認フラグを設定するメソッドを含むサービスクラスを持っています。TransactionRequiredException:更新/削除クエリの実行

@Service("userRolesService") 
@Repository 
@Transactional 
public class UserRolesService { 
    public void verify() { 
     repository.verifyUser(); 
    } 
} 

私のリポジトリはSpringData CrudRepositoryあり、そしてverifyUserは

@Modifying 
@Query("UPDATE user SET (verified = 1 WHERE verified=0)") 
public void verifyUser(); 

ユニットテストでコードを直接呼び出すとき、すべてが正常に動作のようなものです。アプリケーションを介して私の認証プロバイダからそれを呼び出すときに、私は次の例外を取得:

javax.persistence.TransactionRequiredException:サービスクラスは私のユニットテストと認証プロバイダの両方に注入されたクエリ

を削除/更新を実行します@Autowiredアノテーションを使用します。テスト自体には興味深いアノテーション自体はなく、認証プロバイダもありません。

私はアイデアが新鮮です。誰かがヒントを持っていれば、とても感謝しています。

EDIT: verifyUser更新スクリプトを呼び出す代わりに、未検証のユーザーをすべて取得するようになりました。検証済みのフラグを設定し、リポジトリのsave()メソッドを使用します。それは機能しますが、非常に醜いので、私はより良い提案にはオープンしています。

EDIT2は:ここに要求ごと

は設定の永続部分で、私はこれが最も適切であると思い、残りは認証だけを扱います。この設定はUnitテストとWebアプリケーションの両方で使用されますが、違いは、データソースが単体テスト用のH2 DBとWebアプリケーション用のMySQLデータベースに組み込まれていることだけです。

<beans [..]> 

    <bean id="entityManagerFactory" 
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
      depends-on="persistenceInitializer"> 
     <property name="dataSource" ref="dataSource"/> 
     <property name="persistenceUnitName" value="jpa"/> 
     <property name="packagesToScan"> 
      <list> 
       <value>com.example.model</value> 
      </list> 
     </property> 
     <property name="jpaVendorAdapter"> 
      <bean class="com.example.persistence.adapter.ConfigurationRetainingHibernateJpaVendorAdapter"> 
       <property name="database" value="${spring.hibernate.database}"/> 
       <property name="generateDdl" value="${spring.hibernate.generateDdl}"/> 
      </bean> 
     </property> 
     <property name="jpaProperties"> 
      <props> 
       <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.DefaultComponentSafeNamingStrategy 
       </prop> 
      </props> 
     </property> 
    </bean> 

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="entityManagerFactory"/> 
    </bean> 

    <jpa:repositories base-package="com.example.persistence.repository"/> 

    <tx:annotation-driven/> 

    <bean id="persistenceInitializer" class="com.example.persistence.init.NoOpInitializer"/> 

</beans> 

さらに私は、Webアプリケーションではなく、ユニットテストである設定があります。

<beans [..]> 

    <bean id="propertyConfigurer" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
     <property name="locations"> 
      <list> 
       <value>classpath:application.properties</value> 
      </list> 
     </property> 
    </bean> 

    <mvc:annotation-driven/> 

    <mvc:default-servlet-handler/> 

    <context:annotation-config/> 

</beans> 
+0

あなたの春を投稿するだろう設定?あなたのTransactionポストプロセッサがBeanを見つけられないようですか? – MarkOfHall

+0

確かに、2つの設定部分が追加されました。 Configはいくつかのファイルに分割されていますが、そのほとんどはこの問題に関連するべきではありません。 – pushy

答えて

8

私はあなたが<context:annotation-config/>を含むコンテキストに<tx:annotation-driven/>を移動した場合、その後、春が拾うだろうと思いあなたの@Transactional<tx:annotation-driven/>は、それが定義されているアプリケーションコンテキスト内のBeanのみをデコレートするポストプロセッサです。詳細は、私の答えhereを参照してください。

+0

それは私の間違いだったかのように動作します。最後に私は両方の答えを使用しましたが、これはコードがうまく動作するように詳細があるようでした:-) – pushy

4

- ここにあなたのapplicationContext.xmlをどのように見えるべきかである - あなたのサービスクラスはまた、リポジトリ

すべきではない: - ここにどのようにあなたのユニットテストクラスがすべきである

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory"/> 
</bean> 
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/> 
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"> 
    <property name="persistenceUnitName" value="persistenceUnit"/> 
    <property name="dataSource" ref="dataSource"/> 
</bean> 

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"> 
    <property name="driverClassName" value="${database.driverClassName}"/> 
    <property name="url" value="${database.url}"/> 
    <property name="username" value="${database.username}"/> 
    <property name="password" value="${database.password}"/> 
    <property name="testOnBorrow" value="true"/> 
    <property name="testOnReturn" value="true"/> 
    <property name="testWhileIdle" value="true"/> 
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/> 
    <property name="numTestsPerEvictionRun" value="3"/> 
    <property name="minEvictableIdleTimeMillis" value="1800000"/> 
    <property name="validationQuery" value="SELECT 1"/> 
    <property name="initialSize" value="1"/> 
    <property name="minIdle" value="1"/> 
    <property name="maxActive" value="10"/> 
    <property name="poolPreparedStatements" value="true"/> 
    <property name="maxOpenPreparedStatements" value="20"/> 
</bean> 

定義する。

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader=WebContextLoader.class, locations = {"classpath:/META-INF/spring/applicationContext.xml", "classpath:/META-INF/spring/applicationContext-test-override.xml"}) 
public class MyTest { 

- applicationContext-test-override.xmlの使用に注意してください。 これは、テストのためにコンテキストの設定を上書きするために使用されます。このようにすると、実際のアプリケーションコンテキストをテストしていることを意味します。そこで、間違いを犯すと、それがテストに反映されます。 src/test/resourcesに配置する必要があります。これは、あなたがうまくいけば、必要なすべてのです:

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"> 
    <property name="url" value="${database-test.url}"/> 
</bean> 

- (オプション)モードを使用する= AspectJの

は、Mavenのプラグインに次の行を追加します。ランタイムではなくコンパイル時にアスペクトを組み込んでいます(mode = proxy)。

 <plugin> 
      <groupId>org.codehaus.mojo</groupId> 
      <artifactId>aspectj-maven-plugin</artifactId> 
      <version>1.4</version> 
      <dependencies> 
       <dependency> 
        <groupId>org.aspectj</groupId> 
        <artifactId>aspectjrt</artifactId> 
        <version>${aspectj.version}</version> 
       </dependency> 
       <dependency> 
        <groupId>org.aspectj</groupId> 
        <artifactId>aspectjtools</artifactId> 
        <version>${aspectj.version}</version> 
       </dependency> 
      </dependencies> 
      <executions> 
       <execution> 
        <goals> 
         <goal>compile</goal> 
         <goal>test-compile</goal> 
        </goals> 
        <!-- NB: force aspect compile before normal compile, required for 1.3+ 
         see: MASPECTJ-13, MASPECTJ-92 --> 
        <phase>process-sources</phase> 
       </execution> 
      </executions> 
      <configuration> 
       <outxml>true</outxml> 
       <aspectLibraries> 
        <aspectLibrary> 
         <groupId>org.springframework</groupId> 
         <artifactId>spring-aspects</artifactId> 
        </aspectLibrary> 
       </aspectLibraries> 
       <source>${java.version}</source> 
       <target>${java.version}</target> 
      </configuration> 
     </plugin> 
+0

mode = "aspectj" transaction-manager = "transactionManager"をmy tx:annotation - 駆動されたタグ、私は私のユニットテストでもエラーを取得します。それとは別に、applcationContext.xmlへの変更はかなり意味があります。私はそれらを導入しました。それでも、例外はポップアップし続けます。 – pushy

+0

mode = aspectjを回答に追加したときに余分なポイントを追加しました – Solubris

+0

これはうまくいきました。ありがとうございました。スティーブの答えが解決策であることが判明しましたが、私の設定ファイルを修正することは価値があったので、入力に感謝します。 aspectjアノテーションモードが機能しないのですが、プロキシモードでうまく動作します。 – pushy

1

私はいくつかの問題があり、削除または更新を実行するサービスメソッドに@Transactionalアノテーションを追加するだけで解決しました。

0

私も同じ問題に直面し、注釈@Transactional(伝播= Propagation.REQUIRED、rollbackFor = Exception.class、読み取り専用= false)を追加することで解決

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false) 
public class UserRolesService{ 
.......... 
} 
関連する問題