Spring:从TaskExecutor线程

时间:2015-05-28 18:33:20

标签: multithreading spring 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();  
    }
}

因此,在我插入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!

1 个答案:

答案 0 :(得分:9)

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