Spring:从TaskExecutor线程访问JPA存储库

use*_*378 5 spring multithreading hibernate jpa

我正在使用JPA和Hibernate进行持久化,并使用Spring Boot提供的一些自动配置帮助.我正在运行一个JUnit测试,它在JPA存储库中保存了一些记录.然后它实例化一个新的Spring托管线程,它由ThreadPoolTask​​Executor运行.该线程将尝试获取之前添加的记录但没有成功.

以下是测试和可运行线程的相关代码:

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();  
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在我插入rtmpSourceSample对象后,我可以检查它是否已从test方法中插入,它确实在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" />
Run Code Online (Sandbox Code Playgroud)

请注意,因为我使用的是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>
Run Code Online (Sandbox Code Playgroud)

我一直在寻找关于这个有问题的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我从JUnit测试和Runnable类中自动调用AsyncTransactionService.为了从JUnit测试中插入记录,我调用了insertRtmpSource(),然后通过调用getRtmpSources()从我的线程中获取记录.

我试着在没有@Service的情况下做到这一点.这是通过在我的JUnit类上添加带注释的insertRtmpSource()方法和在我的Runnable类上放置getRtmpSources()方法,但它不起作用.

谢谢你快速回复cproinger!

cpr*_*ger 9

线程没有看到记录,因为测试在尚未提交的事务中运行.由于事务绑定到执行线程,因此分叉的任务不使用相同的事务.为了使其工作,插入必须在使用@Transactional(propagation = REQUIRES_NEW)注释的方法中运行.请注意,事务回滚将无法正常工作