복붙노트

[SPRING] 트랜잭션을 위해 JPA EntityManager를 열 수 없습니다. 상자의 예외는 java.lang.IllegalStateException입니다.

SPRING

트랜잭션을 위해 JPA EntityManager를 열 수 없습니다. 상자의 예외는 java.lang.IllegalStateException입니다.

나는 특히 Spring과 Spring-Batch에 상당히 익숙하다. 아직도 나는 어떻게 든 Spring Batch-Admin을 설치할 수 있었다. 지속성을 위해 사용자 정의 작업과 Hibernate / JPA를 추가했습니다.

첫 번째 청크가 유지되어야하는 시점까지 모든 것이 예상대로 작동합니다. 그런 다음 다음과 같은 오류 메시지가 나타납니다.

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]

다음은 전체 스택 추적입니다.

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

동일한 Job이 독립 실행 형 응용 프로그램에서 잘 실행됩니다. 이 문제는 Spring-Batch-Admin 환경에서만 발생합니다. 아래에서 프로젝트 구조 및 종속성을 볼 수 있습니다.

이는 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>

지금까지 내가 이해 한 것은 jobLauncherTaskExecutor 빈이 참조하는 ThreadPoolTaskExecutor와 관련이 있다는 것입니다. 그것은 동시에 작업을 실행하기위한 연결 풀링을 처리하는 것 같다 ...하지만 솔직히 말해서 이런 것들을 작동하도록 구성을 변경하는 방법에 대한 단서가 없다.

[편집] : 저는 그것이 더 확실한 것은 ThreadPoolTaskExecutor입니다. 하지만 그것은 TaskExecutor 인터페이스의 구현 인 것처럼 보입니다.

누군가가 유사한 문제에 부딪혔거나 내 영속성 메소드를 위해 트랜잭션을 생성 할 수있는 방법으로 내 애플리케이션을 구성하는 방법에 대한 제안이 있다면 : 힌트를주세요!

해결법

  1. ==============================

    1.이 오류는 JpaTransactionManager 403 행에 있습니다.

    이 오류는 JpaTransactionManager 403 행에 있습니다.

    TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
    

    이 오류는 트랜잭션 관리자가 엔티티 관리자가 아닌 데이터 소스를 스레드에 바인드하려고하지만 데이터 소스가 이미 있고 예상치 못한 결과라는 것을 의미합니다.

    트랜잭션 관리자는 Entity Manager를 스레드에 아직 바인딩하지 않았으며 JpaTransactionManager 줄 416에서 다음에 발생합니다.

    가능한 설명은 두 가지입니다.

    한 가지 질문은 하나의 실행 스레드에서만 발생합니까 아니면 여러 개의 실행 스레드에서 발생합니까?

    문제가 무엇인지 알아 내려면 다음 단계를 수행하십시오.

  2. ==============================

    2.이것은 일반적으로 여러 트랜잭션 관리자가있는 경우에 발생합니다.

    이것은 일반적으로 여러 트랜잭션 관리자가있는 경우에 발생합니다.

  3. ==============================

    3.이러한 종류의 문제는 jdk 6 또는 더 낮은 버전과 같은 java의 이전 버전에서 발생합니다 .7 이상으로 jdk 버전을 업그레이드하십시오. 심지어 jdk 버전을 7로 업데이트했을 때도 이전과 같은 문제가 발생했습니다.

    이러한 종류의 문제는 jdk 6 또는 더 낮은 버전과 같은 java의 이전 버전에서 발생합니다 .7 이상으로 jdk 버전을 업그레이드하십시오. 심지어 jdk 버전을 7로 업데이트했을 때도 이전과 같은 문제가 발생했습니다.

  4. ==============================

    4.JPA 용 스프링 배치 구성을 구현하여 비슷한 문제를 해결할 수있었습니다.

    JPA 용 스프링 배치 구성을 구현하여 비슷한 문제를 해결할 수있었습니다.

    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);
        }
    }
    

    https : //github.com/hantsy/spring4-sandbox/blob/master/batch-jpa/src/main/java/com/hantsylabs/example/spring/config/JpaBatchConfigurer.java에서 복사했습니다.

  5. from https://stackoverflow.com/questions/20907206/could-not-open-jpa-entitymanager-for-transaction-nested-exception-is-java-lang by cc-by-sa and MIT license