当前位置: 首页 > 知识库问答 >
问题:

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

尹俊贤
2023-03-14

我使用JPA和Hibernate进行持久化,并有Spring Boot的一些自动配置帮助。我正在运行一个JUnit测试,它在JPA Repository中保存了一些记录。然后它实例化一个新的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.html" target="_blank">springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
    parent="abstractVendorAdaptor">
</bean>

<context:annotation-config />
<tx:annotation-driven />
<context:component-scan base-package="guiatv.persistence" />

请注意,持久性单元。xml是不需要的,因为我使用的是Spring Boot。

最后,这是任务执行器 Bean 和可运行线程的 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 存储库。也行不通。

那么,有什么解决方案可以满足我的需求吗?谢谢!

解决方案(来自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()从我的线程中获取记录。

我试着在没有@服务的情况下做这件事。这是通过在我的JUnit类上放置一个带注释的insertRtmpSource()方法,在我的Runnable类上放置一个getRtmpSources()方法,但是它不起作用。

感谢您的快速回复!

共有1个答案

乌甫
2023-03-14

线程看不到记录,因为测试在尚未提交的事务中运行。因为事务被绑定到执行线程,所以被分叉的任务不使用相同的事务。为了实现这一点,插入必须在用@ Transactional(propagation = REQUIRES _ NEW)注释的方法中运行。请注意,那时事务性回滚将不起作用

 类似资料:
  • 我以前从未将Spring Data JPA与存储过程一起使用过,但我非常感谢这里的任何反馈/输入

  • 问题内容: 我试图了解如何在使用Spring进行事务管理的Java应用程序中实现线程。我已经在Spring文档中找到TaskExecutor部分,并且ThreadPoolTask​​Executor看起来很适合我的需求。 ThreadPoolTask​​Executor 该实现只能在Java 5环境中使用,也是该环境中最常用的一种。它公开了用于配置java.util.concurrent.Thre

  • 问题内容: 是否有人从Oracle存储过程成功访问了Web服务?如果是这样,那是Java存储过程吗?PL / SQL存储过程? 有什么原因为什么我不应该尝试从存储的proc访问WS? 这是我到目前为止发现的几个参考 数据库Web服务 从Java存储过程调用外部Web服务 为了澄清,这是针对SOAP调用的 问题答案: 首先,您要调用哪种Web服务?我假设是SOAP或REST。 对于REST Web服

  • 我在vue文件中的“方法”中有这个 我得到以下错误:未捕获(在promise中)TypeError:无法读取未定义的属性'$store'

  • 我正在尝试将应用程序在Azure上投入生产。此应用程序由一个webapp(运行Docker容器)组成,该应用程序应该与Azure存储帐户(blob)通信。 将Web应用的所有出站IP地址添加到我的存储帐户的防火墙设置后,我仍然没有访问Blob的权限。错误如下:“此请求无权执行此操作。” 然而,当我在本地运行我的应用程序,并将我的本地IP添加到这些相同的防火墙设置中时,它可以工作。当我没有将其添加到

  • 问题内容: 我正在使用Spring Data(JPA)存储库来处理CRUD样板。 我这样定义我的存储库接口: 然后Spring自动为我生成上述接口的实现。我们得到的是代理,但我相信最终我们可以归结为。 如果 基础目标类是线程安全的, 则 A 是线程安全的。因此,问题是:线程安全吗? 问题答案: 通常,是的。假设是一个托管对象,我们将从Spring的工厂类(如果您使用Spring作为容器)或CDI托