无法打开JPA EntityManager进行事务处理;嵌套异常是java.lang.IllegalStateException [英] Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException

查看:2624
本文介绍了无法打开JPA EntityManager进行事务处理;嵌套异常是java.lang.IllegalStateException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Spring和Spring-Batch尤其新颖。
我仍​​然设法安装 Spring Batch-Admin 。我添加了自定义作业,并为持久性添加了 Hibernate / JPA

一切都按预期工作,直到第一块应该被保留。然后我收到以下错误消息:

  org.springframework.transaction.CannotCreateTransactionException:
无法打开JPA EntityManager交易;

嵌套异常是java.lang.IllegalStateException:已经有值
[org.springframework.jdbc.datasource.ConnectionHolder@60d31437]
for key [org.springframework.jdbc.datasource .DriverManagerDataSource @ 12da4b19]
绑定到线程[jobLauncherTaskExecutor-1]

这是 full stacktrace

  org.springframework.transaction.CannotCreateTransactionException:无法打开JPA EntityManager进行事务处理;嵌套异常是java.lang.IllegalStateException:已经值org.springframework.jdbc.datasource.ConnectionHolder@43f9e588用于键[org.springframework.jdbc.datasource.DriverManagerDataSource@84f171a]结合到线程[jobLauncherTaskExecutor-1] 
。在在org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:427)
。在组织。 springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
处org.springframework.aop org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
。 framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
在com.sun.proxy。$ Proxy41.saveIfUnique(Unknown Source)
at com.qompa.batch.ArticleItemWriter.write(ArticleItemWriter.java:28)
at org.springframework.batch.core.step .item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:171)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:150)
at org.springframework.batch .core.step.item.FaultTolerantChunkProcessor $ 3.doWithRetry(FaultTolerantChunkProcessor.java:313)
在org.springframework.batch.retry.support.RetryTemplate.doExecute(RetryTemplate.java:240)
。在组织。 springframework.batch.retry.support.RetryTemplate.execute(RetryTemplate.java:187)
在org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:213)
。在org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.write(FaultTolerantChunkProcessor.java:402)
at org.springframework.batch.core.step.item.SimpleChu nkProcessor.process(SimpleChunkProcessor.java:194)
在org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:74)
在org.springframework.batch.core。 step.tasklet.TaskletStep $ ChunkTransactionCallback.doInTransaction处org.springframework.batch org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
(TaskletStep.java:386)
。 core.step.tasklet.TaskletStep $ 2.doInChunkContext(TaskletStep.java:264)
在org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:76)
。在组织.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:367)
在org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:214)
。在组织.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:143)
at org.springframewo rk.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:250)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:195)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:135)
在org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume (SimpleFlow.java:144)
在org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
在org.springframework.batch.core.job .flow.FlowJob.doExecute(FlowJob.java:135)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:281)
at org.springframework.batch.core .launch.support.SimpleJobLauncher $ 1.run(SimpleJobLauncher.java:120)
在java.util.concurrent .ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)$ java.util.concurrent.ThreadPoolExecutor
$ Worker.run(ThreadPoolExecutor.java:615)$ b $在java.lang.Thread.run(Thread.java :724)
引起的:java.lang.IllegalStateException:已经值org.springframework.jdbc.datasource.ConnectionHolder@43f9e588用于键[org.springframework.jdbc.datasource.DriverManagerDataSource@84f171a]结合到线程[ jobLauncherTaskExecutor-1]
。在org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:189)
。在org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:402)
... 36 more

同一个Job在独立应用程序中执行得很好。该问题仅在Spring-Batch-Admin环境中发生。您可以在下面看到项目结构和相关性


这是覆盖/扩展批处理配置的app-context.xml:

 <?xml version =1.0encoding =UTF-8?> 
< beans xmlns =http://www.springframework.org/schema/beans
xmlns:xsi =http://www.w3.org/2001/XMLSchema-instancexmlns :batch =http://www.springframework.org/schema/batch
xmlns:context =http://www.springframework.org/schema/context
xmlns:task = http://www.springframework.org/schema/taskxmlns:jdbc =http://www.springframework.org/schema/jdbc
xmlns:tx =http://www.springframework。 org / schema / tx
xsi:schemaLocation =http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org / schema / jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework。 org / schema / beans / spring-beans-3.0.xsd
http://www.springframework.org/schema/context http:// w ww.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-任务3.2.xsd>

< context:component-scan base-package =com.company.batch/>

< context:property-placeholder location =classpath:batch.properties/>

< import resource =classpath:/META-INF/spring/batch/jobs/article-job.xml/>

< bean id =dataSource
class =org.springframework.jdbc.datasource.DriverManagerDataSource>
< property name =driverClassNamevalue =$ {batch.jdbc.driver}/>
< property name =urlvalue =$ {batch.jdbc.url}/>
< property name =usernamevalue =$ {batch.jdbc.user}/>
< property name =passwordvalue =$ {batch.jdbc.password}/>
< / bean>

< bean id =jdbcTemplateclass =org.springframework.jdbc.core.JdbcTemplate>
< property name =dataSourceref =dataSource>< / property>
< / bean>

< bean id =entityManagerFactory
class =org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean>
< property name =dataSourceref =dataSource/>
< property name =packagesToScanvalue =com.qompa.batch/>
< property name =jpaVendorAdapter>
< bean class =org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter>
< property name =databasevalue =POSTGRESQL>< / property>
< property name =showSqlvalue =true/>
< property name =generateDdlvalue =false/>
< property name =databasePlatformvalue =com.company.utils.persistence.CustomPGDialect/>
< / bean>
< / property>
< property name =jpaProperties>
<道具>
< prop key =hibernate.hbm2ddl.auto>< / prop>
< /道具>
< / property>
< / bean>

< bean id =txManagerclass =org.springframework.orm.jpa.JpaTransactionManager>
< property name =entityManagerFactoryref =entityManagerFactory/>
< / bean>
< tx:annotation-driven transaction-manager =txManager/>

<! - - 计划任务 - >
cron =0 0 * / 4 * * */>
cron =0 15 * / 4 * * */>
< / beans>

到目前为止我所了解的是它与ThreadPoolTask​​Executor 以该 jobLauncherTaskExecutor bean引用。它似乎处理连接池用于同时运行作业 ...但说实话,我不知道如何更改我的配置以使这些工作正常。



:我甚至不确定它是否是后面提到的ThreadPoolTask​​Executor。但它似乎是实施 TaskExecutor


如果有人遇到类似的问题,或者有建议如何配置我的应用程序,以便可以为我的持久性方法创建事务:请给我一个提示!

解决方案

错误来自JpaTransactionManager第403行:

  TransactionSynchronizationManager.bindResource(getDataSource(),conHolder); 

该错误意味着事务管理器试图绑定数据源(不是实体管理器)到线程,但数据源已经在那里,这是意想不到的。注意事务管理器还没有开始将实体管理器绑定到线程,这将发生在JpaTransactionManager第416行:



有两种可能的解释:


  • 有人(另一个事务管理器?)将数据源添加到事务管理器之前的线程并且这是意外的。


  • 或者没有人向事务管理器添加数据源,只是在任务执行结束时没有人在将其返回到池之前清理线程,可能是由于错误或未处理的异常。




这是否也会发生只有一个执行线程,或者只有当有几个?



为了找出问题所在,这些是一些步骤:




I am quite new to Spring and Spring-Batch in particular. Still I somehow managed to install the Spring Batch-Admin. I added custom jobs and Hibernate/JPA for persistence.

Everything is working as expected, up to the point where the first chunk should be persisted. Then I receive the following error-message:

org.springframework.transaction.CannotCreateTransactionException: 
      Could not open JPA  EntityManager for transaction;

nested exception is java.lang.IllegalStateException: Already value
      [org.springframework.jdbc.datasource.ConnectionHolder@60d31437] 
      for key [org.springframework.jdbc.datasource.DriverManagerDataSource@12da4b19] 
      bound to thread [jobLauncherTaskExecutor-1]

This is the full stacktrace:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA  EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@43f9e588] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@84f171a] bound to thread [jobLauncherTaskExecutor-1]
     at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:427)
     at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
     at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
     at com.sun.proxy.$Proxy41.saveIfUnique(Unknown Source)
     at com.qompa.batch.ArticleItemWriter.write(ArticleItemWriter.java:28)
     at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:171)
     at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:150)
     at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$3.doWithRetry(FaultTolerantChunkProcessor.java:313)
     at org.springframework.batch.retry.support.RetryTemplate.doExecute(RetryTemplate.java:240)
     at org.springframework.batch.retry.support.RetryTemplate.execute(RetryTemplate.java:187)
     at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:213)
     at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.write(FaultTolerantChunkProcessor.java:402)
     at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:194)
     at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:74)
     at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:386)
     at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
     at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:264)
     at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:76)
     at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:367)
     at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:214)
     at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:143)
     at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:250)
     at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:195)
     at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:135)
     at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
     at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
     at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
     at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
     at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
     at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:281)
     at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
     at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@43f9e588] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@84f171a] bound to thread [jobLauncherTaskExecutor-1]
     at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:189)
     at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:402)
... 36 more

The same Job executes fine in a standalone application. The problem occurs only in the Spring-Batch-Admin environment. Below you can see the project structure and dependencies:

This is the app-context.xml that overrides/extends the Batch-Admin configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">

    <context:component-scan base-package="com.company.batch" />

    <context:property-placeholder location="classpath:batch.properties" />

    <import resource="classpath:/META-INF/spring/batch/jobs/article-job.xml" />

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${batch.jdbc.driver}" />
        <property name="url" value="${batch.jdbc.url}" />
        <property name="username" value="${batch.jdbc.user}" />
        <property name="password" value="${batch.jdbc.password}" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.qompa.batch" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="POSTGRESQL"></property>
                <property name="showSql" value="true" />
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="com.company.utils.persistence.CustomPGDialect" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto"></prop>
            </props>
        </property>
    </bean>

    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="txManager" />

    <!-- schedule tasks -->
    <task:scheduled-tasks>
        <task:scheduled ref="articleRetrieval" method="run"
            cron="0 0 */4 * * *" />
        <task:scheduled ref="articleConversion" method="run"
            cron="0 15 */4 * * *" />
    </task:scheduled-tasks>
</beans>

What I understand so far is that it has to do with the ThreadPoolTaskExecutor to which the jobLauncherTaskExecutor bean refers. It seems to handle connection pooling for concurrently running jobs ... but to be honest I have no clue how to change my configurations to make these things work.

[Edit]: I am not even sure wether it is the afromentioned ThreadPoolTaskExecutor. But it seem s to be an implementation of the TaskExecutor interface.

If anyone ran into a similar issue, or has a suggestion how to configure my application in a way that transactions can be created for my persistence methods: Please give me a hint!

解决方案

The error comes from JpaTransactionManager line 403:

TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);

The error means that the transaction manager is trying to bind the datasource (not the entity manager) to the thread, but the datasource is already there and this is unexpected.

Note that the transaction manager had not started yet to bind the Entity Manager to the thread, that would happen next at JpaTransactionManager line 416:

There are two possible explanations:

  • Somebody (another transaction manager?) is adding the datasource to the thread before the transaction manager and this is unexpected.

  • Or noone is adding the datasource to the transaction manager, is just that at the end of the task execution noone cleans the thread before returning it to the pool, maybe due an error or an unhandled exception.

One question, does this also happen for only one execution thread, or only when there are several?

To find out what the problem is, these are some steps:

  • run with a minimal number of threads that cause the problem

  • put a breakpoint in TransactionSynchronizationManager.bindResource() to see who adds the connection to the thread. The breakpoint can be a conditional breakpoint with a condition on the thread name: "jobLauncherTaskExecutor-1".equals(Thread.currentThread().getName())

  • put also a breakpoint in TransactionSynchronizationManager.unbindResource(), to see if the datasource is unbound from the thread. when the breakpoints hit, scroll down the stacktrace and see which classes are causing this.

这篇关于无法打开JPA EntityManager进行事务处理;嵌套异常是java.lang.IllegalStateException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆