我使用JPA和Hibernate进行持久化,并使用Spring Boot提供的一些自动配置帮助。我正在运行JUnit测试,该测试在JPA存储库中保存了一些记录。然后它实例化一个新的Spring托管线程,它由ThreadPoolTaskExecutor运行。该线程将尝试获取之前添加的记录但没有成功。
以下是测试和可运行线程的相关代码:
public class RtmpSpyingTests extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
ThreadPoolTaskExecutor rtmpSpyingTaskExecutor;
@Autowired
ApplicationContext ctx;
@Autowired
RtmpSourceRepository rtmpRep;
@Test
public void test() {
RtmpSource rtmpSourceSample = new RtmpSource("test");
rtmpRep.save(rtmpSourceSample);
rtmpRep.flush();
List<RtmpSource> rtmpSourceList = rtmpRep.findAll(); // Here I get a list containing rtmpSourceSample
RtmpSpyingTask rtmpSpyingTask = ctx.getBean(RtmpSpyingTask.class,
"arg1","arg2");
rtmpSpyingTaskExecutor.execute(rtmpSpyingTask);
}
}
public class RtmpSpyingTask implements Runnable {
@Autowired
RtmpSourceRepository rtmpRep;
String nameIdCh;
String rtmpUrl;
public RtmpSpyingTask(String nameIdCh, String rtmpUrl) {
this.nameIdCh = nameIdCh;
this.rtmpUrl = rtmpUrl;
}
public void run() {
// Here I should get a list containing rtmpSourceSample, but instead of that
// I get an empty list
List<RtmpSource> rtmpSource = rtmpRep.findAll();
}
}
因此,在我插入rtmpSourceSample对象后,我可以检查它是否已从测试方法中插入,它确实在rtmpSourceList列表中。但是,当我从线程中做同样的事情时,我得到的是一个空列表。
以下是spring-context.xml配置文件中的JPA / Hibernate配置:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MVCC=true" />
<property name="username" value="user" />
<property name="password" value="user" />
</bean>
<!-- Define the JPA transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<constructor-arg ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="vendorAdaptor" />
<property name="packagesToScan" value="guiatv.persistence.domain" />
</bean>
<bean id="abstractVendorAdaptor" abstract="true">
<property name="generateDdl" value="true" />
<property name="database" value="H2" />
<property name="showSql" value="false" />
</bean>
<bean id="entityManager"
class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="vendorAdaptor"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
parent="abstractVendorAdaptor">
</bean>
<context:annotation-config />
<tx:annotation-driven />
<context:component-scan base-package="guiatv.persistence" />
请注意,因为我使用Spring Boot,所以不需要persistence-unit.xml。
最后这是taskexecutor bean和runnable线程的xml配置:
<bean id="rtmpSpyingTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="5" />
<property name="queueCapacity" value="5" />
</bean>
<bean id="rtmpSpyingTask" class="guiatv.realtime.rtmpspying.RtmpSpyingTask"
scope="prototype">
<constructor-arg ref="rtmpSpyingTaskExecutor" />
</bean>
我一直在寻找有关Spring有问题的持久性和线程组合的主题。我到目前为止找到的解决方案之一是使用@Transactional方法创建一些@Service注释类,我应该从run()方法调用它。它对我不起作用。 其他一些解决方案涉及使用EntityManager或其他一些依赖Hibernate的bean,而不是直接查询JPA Repository。也不行。
那么,任何能满足我需求的解决方案呢?谢谢!
解决方案(来自cproinger):
创建一个@Service注释类:
@Service
public class AsyncTransactionService {
@Autowired
RtmpSourceRepository rtmpRep;
@Transactional(readOnly = true)
public List<RtmpSource> getRtmpSources() {
return rtmpRep.findAll();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertRtmpSource(RtmpSource rtmpSource) {
rtmpRep.save(rtmpSource);
}
}
然后,我从JUnit测试和Runnable类中自动调用AsyncTransactionService。为了从JUnit测试中插入记录,我调用了insertRtmpSource(),然后通过调用getRtmpSources()从我的线程中获取记录。
我试着在没有@Service的情况下做到这一点。这是通过在我的JUnit类上添加带注释的insertRtmpSource()方法和在我的Runnable类上放置getRtmpSources()方法,但它不起作用。
感谢您快速回复cproinger!
答案 0 :(得分:9)
线程看不到记录,因为测试在尚未提交的事务中运行。由于事务绑定到执行线程,因此分叉的任务不使用相同的事务。为了使其工作,插入必须在使用@Transactional(propagation = REQUIRES_NEW)注释的方法中运行。请注意,事务回滚将无效,但