无法为事务打开 JPA EntityManager;嵌套异常是 java.lang.IllegalStateException
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20907206/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException
提问by achingfingers
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/JPAfor persistence.
我对 Spring 和 Spring-Batch 尤其陌生。尽管如此,我还是设法安装了Spring Batch-Admin。我添加了自定义作业和Hibernate/JPA以实现持久性。
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.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.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.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:
相同的作业在独立应用程序中执行良好。该问题仅出现在 Spring-Batch-Admin 环境中。您可以在下面看到项目结构和依赖项:
This is the app-context.xml that overrides/extends the Batch-Admin configuration:
这是覆盖/扩展 Batch-Admin 配置的 app-context.xml:
<?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 ThreadPoolTaskExecutorto 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.
到目前为止我所理解的是它与bean 所引用的ThreadPoolTaskExecutor有关jobLauncherTaskExecutor
。它似乎处理并发运行的作业的连接池......但说实话,我不知道如何更改我的配置以使这些事情起作用。
[Edit]: I am not even sure wether it is the afromentioned ThreadPoolTaskExecutor. But it seem s to be an implementation of the TaskExecutorinterface.
[编辑]:我什至不确定它是否是 afro提到的 ThreadPoolTaskExecutor。但它似乎是TaskExecutor接口的实现。
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!
如果有人遇到类似的问题,或者有关于如何以可以为我的持久性方法创建事务的方式配置我的应用程序的建议:请给我一个提示!
采纳答案by Angular University
The error comes from JpaTransactionManager line 403:
错误来自 JpaTransactionManager 第 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:
请注意,事务管理器尚未开始将实体管理器绑定到线程,接下来将在 JpaTransactionManager 第 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 breakpointwith 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.
使用最少数量的导致问题的线程运行
放置一个断点
TransactionSynchronizationManager.bindResource()
以查看谁将连接添加到线程。断点可以是在线程名称上带有条件的条件断点:“jobLauncherTaskExecutor-1”.equals(Thread.currentThread().getName())在 中也放置一个断点
TransactionSynchronizationManager.unbindResource()
,以查看数据源是否与线程解除绑定。当断点命中时,向下滚动堆栈跟踪并查看哪些类导致了这种情况。
回答by Sinto
This normally happens when you have multiple transaction managers in place.
当您有多个事务管理器时,通常会发生这种情况。
一些提示..使用注解@EnableBatchProcessing 时,Spring Batch 会自动注册一个事务管理器,您的 JpaTransactionManager 可能永远不会被使用。如果要更改spring批处理使用的事务管理器,则必须实现接口BatchConfigurer。(https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-3-profiles-and-environments/https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-3-profiles-and-environments/)。You can specify transaction manager for tasklets as follows:
您可以为 tasklet 指定事务管理器,如下所示:
<tasklet transaction-manager="transactionManager">
回答by mad_fox
I was able to solve a similar issue by implementing a spring batch configuration for JPA
我能够通过为 JPA 实现 spring 批处理配置来解决类似的问题
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.BatchConfigurationException;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
import org.springframework.batch.core.explore.support.MapJobExplorerFactoryBean;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class JpaBatchConfigurer implements BatchConfigurer {
private static final Logger logger = LoggerFactory
.getLogger(JpaBatchConfigurer.class);
@Inject
private DataSource dataSource;
@Inject
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;
protected JpaBatchConfigurer() {
}
@Override
@Bean
public JobRepository getJobRepository() {
return jobRepository;
}
@Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
@Override
@Bean
public JobLauncher getJobLauncher() {
return jobLauncher;
}
@Override
@Bean
public JobExplorer getJobExplorer() {
return jobExplorer;
}
@PostConstruct
public void initialize() {
try {
if (dataSource == null) {
logger.warn("No datasource was provided...using a Map based JobRepository");
if (this.transactionManager == null) {
this.transactionManager = new ResourcelessTransactionManager();
}
MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(
this.transactionManager);
jobRepositoryFactory.afterPropertiesSet();
this.jobRepository = jobRepositoryFactory.getObject();
MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(
jobRepositoryFactory);
jobExplorerFactory.afterPropertiesSet();
this.jobExplorer = jobExplorerFactory.getObject();
} else {
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
}
this.jobLauncher = createJobLauncher();
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
private JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor( new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setIsolationLevelForCreate("ISOLATION_SERIALIZABLE");
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setValidateTransactionState(false);
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public JobBuilderFactory jobBuilderFactory(JobRepository jobRepository){
return new JobBuilderFactory(jobRepository);
}
@Bean
public StepBuilderFactory stepBuilderFactory(JobRepository jobRepository, PlatformTransactionManager transactionManager){
return new StepBuilderFactory(jobRepository, transactionManager);
}
}
This was copied from:https://github.com/hantsy/spring4-sandbox/blob/master/batch-jpa/src/main/java/com/hantsylabs/example/spring/config/JpaBatchConfigurer.java
这是复制自:https: //github.com/hantsy/spring4-sandbox/blob/master/batch-jpa/src/main/java/com/hantsylabs/example/spring/config/JpaBatchConfigurer.java
回答by Jeevitha
These kind of problems occur with older version of java like jdk 6 or further lower versions.Upgrade your jdk version to 7 or above. Even i do had the same kinda issue before which gone when i updated my jdk version to 7.
这类问题出现在较旧版本的 java 中,例如 jdk 6 或更低版本。将 jdk 版本升级到 7 或更高版本。即使我确实遇到了同样的问题,但当我将 jdk 版本更新为 7 时就消失了。
回答by G.Dileo
If you have 2 dataSource, I suggest you to read:
如果您有 2 个数据源,我建议您阅读:
https://github.com/spring-projects/spring-boot/issues/3012
https://github.com/spring-projects/spring-boot/issues/3012
So... configure the main datasource (is important the transaction manager's name)
所以...配置主数据源(事务管理器的名字很重要)
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager",
basePackages = "a.b.c")
@PropertySource({"classpath:db_persistence.properties"})
@EnableTransactionManagement
and the other datasource:
和另一个数据源:
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "another_EntityManager",
transactionManagerRef = "another_transactionManager",
basePackages = "x.y.z")
@PropertySource({"classpath:db_persistence.properties"})
@EnableTransactionManagement
I hope that this help you.
我希望这对你有帮助。