在试图解决以下问题的同时,在过去几天里,白发的数量急剧增加。我在使用简单的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之前事务将被提交。 –