Je suis assez nouveau pour Spring et Spring-Batch en particulier. J'ai quand même réussi à installer Spring Batch-Admin. J'ai ajouté des travaux personnalisés et Hibernate/JPA pour la persistance.
Tout fonctionne comme prévu, jusqu'au point où le premier bloc devrait être conservé. Ensuite, je reçois le message d'erreur suivant:
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]
C'est le 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
Le même travail s'exécute correctement dans une application autonome. Le problème se produit uniquement dans l'environnement Spring-Batch-Admin. Ci-dessous, vous pouvez voir la structure et les dépendances du projet :
C'est le fichier app-context.xml qui remplace/étend la configuration de Batch-Admin:
<?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>
Ce que je comprends jusqu’à présent, c’est que cela a à voir avec le ThreadPoolTaskExecutor auquel le bean jobLauncherTaskExecutor
fait référence. Il semble gérer le regroupement de connexions pour les travaux en cours d'exécution simultanés ... mais pour tout vous dire, je ne sais pas comment changer mes configurations pour que ces choses fonctionnent.
[Edit]: Je ne suis même pas sûr que ce soit le ThreadPoolTaskExecutor afromentioned. Mais cela semble être une implémentation de l'interface TaskExecutor .
Si quelqu'un rencontre un problème similaire ou a une suggestion sur la manière de configurer mon application de manière à ce que des transactions puissent être créées pour mes méthodes de persistance: Donnez-moi un indice!
L'erreur provient de la ligne 403 de JpaTransactionManager:
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
L'erreur signifie que le gestionnaire de transactions tente de lier le datasource (pas le gestionnaire d'entités) au thread, mais que la source de données est déjà présente et que cela est inattendu.
Notez que le gestionnaire de transactions n'avait pas encore commencé à lier Entity Manager au thread, ce qui se produirait ensuite à la ligne JpaTransactionManager 416:
Il y a deux explications possibles:
Quelqu'un (un autre gestionnaire de transactions?) Ajoute la source de données au thread avant le gestionnaire de transactions, ce qui est inattendu.
Ou que personne n’ajoute la source de données au gestionnaire de transactions, c’est simplement qu’à la fin de l’exécution de la tâche, personne ne nettoie le thread avant de le retourner au pool, peut-être à cause d’une erreur ou d’une exception non gérée.
Une question, cela se produit-il également pour un seul thread d’exécution ou s’il en existe plusieurs?
Pour savoir quel est le problème, voici quelques étapes:
exécuter avec un nombre minimal de threads à l'origine du problème
placez un point d'arrêt dans TransactionSynchronizationManager.bindResource()
pour voir qui ajoute la connexion au thread. Le point d'arrêt peut être un point d'arrêt conditionnel avec une condition sur le nom du thread: "jobLauncherTaskExecutor-1" .equals (Thread.currentThread (). GetName ())
placez également un point d'arrêt dans TransactionSynchronizationManager.unbindResource()
, pour voir si la source de données est non liée au thread. Lorsque les points d'arrêt atteignent, faites défiler la pile et tracez les classes responsables.
Ce type de problèmes survient avec une ancienne version de Java telle que jdk 6 ou d'autres versions inférieures. Mettez à niveau votre version de jdk vers la version 7 ou une version ultérieure. Même si j'avais le même type de problème avant lequel je suis parti quand j'ai mis à jour ma version de jdk à 7.