3

次の問題を解決しようとしている間、ここ数日で白髪の量が劇的に増加しました. シンプルなSpring 3.2イベントメカニズムを利用するカスタムイベントリスナーでSpring Data JPAリポジトリを使用しています。私が抱えている問題ListenerAは、エンティティを作成して呼び出しassetRepository.save(entity)、またはassetRepository.saveAndFlash(entity)別のリスナーからこの同じエンティティを取得するための後続の呼び出しが失敗することです。原因はListenerB、データベース内に元のエンティティが見つからないためと思われます。Hibernate のキャッシュにまだ残っているようです。ListenerB がエンティティをロックするトリガーは、スレッド プールからの実行可能なタスクの実行の結果として発生するイベントです。これが私の構成です:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceUnitName" value="spring-jpa" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="false" />
            <property name="database" value="#{appProps.database}" />
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">#{appProps['hibernate.hbm2ddl.auto']}</prop>
            <prop key="hibernate.show_sql">#{appProps['hibernate.show_sql']}</prop>
            <prop key="hibernate.format_sql">#{appProps['hibernate.format_sql']}</prop>
            <prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.impl.FSDirectoryProvider</prop>
            <prop key="hibernate.search.default.indexBase">#{appProps.indexLocation}</prop>
            <prop key="hibernate.search.lucene_version">#{appProps['hibernate.search.lucene_version']}</prop>
        </props>
    </property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

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

Oracle データベースへの接続を定義dataSourceする のインスタンスである構成は省略しています。ComboPooledDataSource補足として、コンポーネント スキャンが使用され、プロジェクトは Spring MVC です。今Javaクラス。

リスナーA

@Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {

    @Autowired
    private AssetRepository assetRepository;

    @Autowired
    private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor

    @Override
    @Transactional
    public void onApplicationEvent(FileUploadedEvent event) {

    Asset target = event.getTarget();
    Job job = new Job(target);
    assetRepository.save(job);

    executor.execute(job);
}

リスナーB

@Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {

    @Autowired
    private AssetRepository assetRepository;


    @Override
    @Transactional
    public void onApplicationEvent(JobStartedEvent event) {

    String id = event.getJobId();
    Job job = assetRepository.findOne(id); // at this point we can not find the job, returns null
    job.setStartTime(new DateTime());
    job.setStatus(Status.PROCESSING);

    assetRepository.save(job);
}

JobStartedEvent内の実行可能なタスクから起動されますTaskExecutor。ここで何が間違っていますか?トランザクション対応のカスタム イベント パブリッシャを使用しようとしましたが、問題が解決しないようです。また、データ リポジトリの代わりに適切なサービスを配線し、リスナーから注釈を削除しようとしましたが@Transactional、これも失敗しました。問題を解決する方法についての合理的な提案は大歓迎です。

4

2 に答える 2

2

@Kresimir Nesekからのヒントのおかげで、問題を解決できました。したがって、解決策は、Spring Data リポジトリを適切なサービスに置き換えることでした。変更されたクラスは次のとおりです。

リスナーA

@Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {

    @Autowired
    private JobService service;

    @Autowired
    private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor

    @Override
    public void onApplicationEvent(FileUploadedEvent event) {

    Job job = service.initJobForExecution(event.getTarget());

    executor.execute(job);
    }
 }

JobServiceメソッドでは、すべてが機能するためinitJobForExecution(Asset target)に注釈を付ける必要があり@Transactional(propagation=Propagation.REQUIRES_NEW)ました。

リスナー B

@Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {

@Autowired
private JobService service;


@Override
public void onApplicationEvent(JobStartedEvent event) {
    service.updateStatus(event.getJobId(), Status.PROCESSING); 
 }
}
于 2013-07-18T13:27:46.470 に答える