【发布时间】:2013-05-30 16:34:18
【问题描述】:
在尝试解决以下问题时,过去几天白发的数量急剧增加。我在使用简单 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>
我省略了dataSource 配置,它是ComboPooledDataSource 的一个实例,它定义了与Oracle 数据库的连接。附带说明一下,使用了组件扫描,并且项目是 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 注释,这也失败了。欢迎任何关于如何解决问题的合理建议。
【问题讨论】:
-
您确定事务注释在 onapplicationevent 方法上起作用吗?如果您在服务 bean 中推送作业的创建并将事务注释移到那里会发生什么?
-
我倾向于认为注释正在工作,因为 Job 已经嵌入了执行期间需要的延迟加载集合。关于第二个问题,我设法通过用服务替换 Spring Data 存储库来解决这个问题。然而,为了让它工作,我必须用
@Transactional(propagation=Propagation.REQUIRES_NEW)注释初始化方法(Job 对象首先创建和保存的地方)。我不是 100% 确定这是否是解决此问题的正确方法。为什么我只能在侦听器中使用存储库? -
这可能不是原因,但无论如何 - 您似乎在退出事务块并提交事务之前调用了 executor.execute(job) 。您不能 100% 保证事务会在 onApplicationEvent 执行之前提交。
标签: spring hibernate spring-mvc event-handling spring-data-jpa