it-swarm.com.de

JPA EntityManager konnte nicht für die Transaktion geöffnet werden. Die verschachtelte Ausnahme ist Java.lang.IllegalStateException

Ich bin besonders im Frühjahr und im Frühjahrs-Batch ziemlich neu. Trotzdem gelang es mir irgendwie, den Spring Batch-Admin zu installieren. Ich habe benutzerdefinierte Jobs und Hibernate/JPA für Persistenz hinzugefügt. 

Alles funktioniert wie erwartet, bis zu dem Punkt, an dem der erste Block bestehen bleiben soll. Dann erhalte ich folgende Fehlermeldung:

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

nested exception is Java.lang.IllegalStateException: Already value
      [[email protected]] 
      for key [[email protected]4b19] 
      bound to thread [jobLauncherTaskExecutor-1]

Dies ist der full stacktrace:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA  EntityManager for transaction; nested exception is Java.lang.IllegalStateException: Already value [[email protected]] for key [[email protected]71a] 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 [[email protected]] for key [[email protected]71a] 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

Derselbe Job wird in einer eigenständigen Anwendung problemlos ausgeführt. Das Problem tritt nur in der Spring-Batch-Admin-Umgebung auf. Nachfolgend sehen Sie die Projektstruktur und Abhängigkeiten:

enter image description here

Dies ist die app-context.xml, die die Batch-Admin-Konfiguration überschreibt/erweitert:

<?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>

Was ich bisher verstehe ist, dass es mit dem ThreadPoolTaskExecutor zu tun hat, auf den sich die jobLauncherTaskExecutor-Bean bezieht. Es scheint das connection pooling für gleichzeitig ausgeführte Jobs zu handhaben ... aber um ehrlich zu sein, habe ich keine Ahnung, wie ich meine Konfigurationen ändern kann, damit diese Dinge funktionieren.

[Bearbeiten]: Ich bin mir nicht mal sicher, ob es sich um den ThreadPoolTaskExecutor handelt. Es scheint jedoch eine Implementierung der TaskExecutor Schnittstelle zu sein.

Wenn jemand auf ein ähnliches Problem gestoßen ist oder einen Vorschlag hat, wie ich meine Anwendung so konfigurieren kann, dass Transaktionen für meine Persistenzmethoden erstellt werden können, geben Sie mir bitte einen Hinweis!

11
achingfingers

Der Fehler stammt aus der JpaTransactionManager-Zeile 403:

TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);

Der Fehler bedeutet, dass der Transaktionsmanager versucht, die datasource (nicht den Entitätsmanager) an den Thread zu binden, aber die Datenquelle ist bereits vorhanden und dies ist unerwartet. 

Beachten Sie, dass der Transaktionsmanager noch nicht gestartet wurde, um den Entity Manager an den Thread zu binden. Dies würde als nächstes in der JpaTransactionManager-Zeile 416 geschehen:

Es gibt zwei mögliche Erklärungen:

  • Jemand (ein anderer Transaktionsmanager?) Fügt dem Thread vor dem Transaktionsmanager die Datenquelle hinzu. Dies ist unerwartet.

  • Oder niemand fügt die Datenquelle zum Transaktionsmanager hinzu. Am Ende der Taskausführung wird jedoch der Thread von niemandem gelöscht, bevor er in den Pool zurückgegeben wird, möglicherweise aufgrund eines Fehlers oder einer nicht behandelten Ausnahme.

Eine Frage, passiert dies auch nur für einen Ausführungsthread oder nur, wenn es mehrere gibt?

Um herauszufinden, was das Problem ist, sind dies einige Schritte:

  • ausführen mit einer minimalen Anzahl von Threads, die das Problem verursachen

  • setzen Sie einen Haltepunkt in TransactionSynchronizationManager.bindResource(), um zu sehen, wer die Verbindung zum Thread hinzufügt. Der Haltepunkt kann ein bedingter Haltepunkt sein mit einer Bedingung für den Threadnamen: "jobLauncherTaskExecutor-1" .equals (Thread.currentThread (). GetName ()) 

  • fügen Sie auch einen Haltepunkt in TransactionSynchronizationManager.unbindResource() ein, um festzustellen, ob die Datenquelle vom Thread nicht gebunden ist. Wenn die Haltepunkte erreicht werden, scrollen Sie im Stacktrace nach unten und sehen Sie, welche Klassen dies verursachen. 

14

Diese Art von Problemen tritt bei älteren Java-Versionen wie jdk 6 oder weiteren niedrigeren Versionen auf. Aktualisieren Sie Ihre jdk-Version auf 7 oder höher. Sogar ich hatte das gleiche Problem vor dem, als ich meine jdk-Version auf 7 aktualisiert hatte. 

0
Jeevitha