在試圖解決以下問題的同時,在過去幾天裏,白髮的數量急劇增加。我在使用簡單的Spring 3.2事件機制的自定義事件偵聽器中使用Spring Data JPA存儲庫。我遇到的問題是,如果ListenerA
創建實體並調用assetRepository.save(entity)
或assetRepository.saveAndFlash(entity)
,則後續調用將從另一個偵聽器檢索到該實體失敗。原因似乎是ListenerB
無法在數據庫中找到原始實體,它似乎仍在Hibernate的緩存中。 ListenerB鎖定實體的觸發器是由於線程池中的可運行任務執行而觸發的事件。 這裏是我的配置:春季數據JPA存儲庫,春季交易和事件發生
<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>
我省略了dataSource
配置,它的ComboPooledDataSource
一個實例,它定義連接Oracle數據庫。作爲一個方面說明,使用了組件掃描,並且該項目是Spring MVC。 現在是Java類。
ListenerA
@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);
}
ListenerB
@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
註釋,這些註釋也失敗了。歡迎任何有關如何解決問題的合理建議。
你確定事務註解在onapplicationevent方法上工作嗎?如果您在服務bean內部創建作業並將事務註釋移到那裏,會發生什麼? –
我傾向於認爲這個註釋正在工作,因爲Job嵌入了執行過程中需要的延遲加載收集。關於第二個問題,我設法通過用服務替換Spring Data存儲庫來解決這個問題。然而,爲了這個工作,我不得不用'@Transactional(propagation = Propagation.REQUIRES_NEW)'註釋初始化方法(其中Job對象首先創建並保存)。如果這是解決此問題的正確方法,我不是100%確定的。爲什麼我只能在聽衆中使用存儲庫? – pilot
這可能不是原因,但無論如何 - 您似乎在退出事務性塊並提交事務之前調用executor.execute(job)。您不能100%保證在執行onApplicationEvent之前事務將被提交。 –