Spring Boot Jpa Batch - CannotCreateTransactionException

时间:2018-05-04 11:05:25

标签: spring-boot jpa spring-data spring-batch datasource

我有一个Spring Boot + JPA + Spring批处理(+ Spring Integration)项目。还有Flyway,配置分开。 我已经将spring批处理配置为使用与默认值不同的数据源" spring.jpa.datasource"。这可以。 在我的tasklet里面,我查询两个数据源,ds1上的第一个查询正确,ds2上的第二个查询出错:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@2fb082ff] for key [HikariDataSource (HikariPool-1)] bound to thread [main]

更多细节: 我有一个每个数据源的配置类,用

注释
@Configuration 
@EnableTransactionManagement
@EnableJpaRepositories

在每个配置中,使用自定义名称定义DataSource,LocalContainerEntityManagerFactoryBean和JpaTransactionManager的bean。

为每个数据源定义一个服务:

 @Service @Transactional(transactionManager=JpaApsConfiguration.APS_TRANSACTION_MANAGER, propagation = Propagation.REQUIRES_NEW)

并使用右限定符自动装配实体管理器 (注意:通过JpaRepository类进行查询)

有什么可能导致此行为的建议?感谢

注意:

  • 数据库成功连接
  • 之前我有过不同的错误 (已经修复,但可能有帮助): ' NoUniqueBeanDefinitionException'因为春天无法识别 纠正' PlatformTransactionManager'在我的两笔交易之间 manager bean和默认的transactionManager。

编辑:

这里是错误的完整堆栈跟踪:

2018-05-08 12:12:24 INFO o.s.batch.core.job.SimpleStepHandler - 执行步骤:[STEP#PROCESS_SHIPMENTS] 2018-05-08 12:12:24错误o.s.batch.core.step.AbstractStep - 在作业PROCESS_APS中遇到执行步骤#PROCESS_SHIPMENTS的错误 org.springframework.transaction.CannotCreateTransactionException:无法打开JPA EntityManager进行事务处理;嵌套异常是java.lang.IllegalStateException:绑定到线程[main]的键[HikariDataSource(HikariPool-1)]的值[org.springframework.jdbc.datasource.ConnectionHolder@351e89fc]     在org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:450)     at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)     在org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)     at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)     在org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)     在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)     在org.springframework.aop.framework.CglibAopProxy $ DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)     at dkr.astreconnector.service.GespeService $$ EnhancerBySpringCGLIB $$ 73dbb368.getValueDetailByUniq()     在dkr.astreconnector.batch.worker.ShipmentProcessor.process(ShipmentProcessor.java:63)     在dkr.astreconnector.batch.worker.ShipmentProcessor.process(ShipmentProcessor.java:30)     在org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126)     在org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:303)     在org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:202)     在org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)     在org.springframework.batch.core.step.tasklet.TaskletStep $ ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)     在org.springframework.batch.core.step.tasklet.TaskletStep $ ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)     在org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)     在org.springframework.batch.core.step.tasklet.TaskletStep $ 2.doInChunkContext(TaskletStep.java:272)     在org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)     在org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)     在org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)     在org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)     在org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)     在org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)     在org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)     在org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:66)     在org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)     在org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)     在org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)     在org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136)     在org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:308)     在org.springframework.batch.core.launch.support.SimpleJobLauncher $ 1.run(SimpleJobLauncher.java:141)     在org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)     在org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:134)     在org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:163)     在org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:179)     在org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:134)     在org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:128)     在org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:790)     在org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)     在org.springframework.boot.SpringApplication.run(SpringApplication.java:335)     在org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)     在org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)     在dkr.astreconnector.AstreConnectorBatchApplication.main(AstreConnectorBatchApplication.java:14) 引起:java.lang.IllegalStateException:绑定到线程[main]的密钥[HikariDataSource(HikariPool-1)]的值[org.springframework.jdbc.datasource.ConnectionHolder@351e89fc]     at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:193)     在org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:425)     ...省略了43个常用帧 2018-05-08 12:12:24 INFO osbclsupport.SimpleJobLauncher - 作业:[FlowJob:[name = PROCESS_APS]]使用以下参数完成:[{run.id = 128}]并且状态如下:[FAILED]

2 个答案:

答案 0 :(得分:0)

有点晚了,但我想回复@mad_fox

主数据库:

@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = JpaAppsConfiguration.APPS_ENTITY_MANAGER,
    transactionManagerRef = "transactionManager",
    basePackages = { JpaAppsConfiguration.APPS_REPOSITORIES })
@EnableAutoConfiguration
public class JpaAppsConfiguration {

public final static String APPS_PERSISTENCE_UNIT = "apps";
public final static String APPS_DATA_SOURCE = "appDs";
public final static String APPS_ENTITY_MANAGER = "appsEmf";
public final static String APPS_TRANSACTION_MANAGER = "appsTm";
protected final static String APPS_PACKAGES = ""; //my models package
protected final static String APPS_REPOSITORIES = ""; //my repos package

@Primary
@Bean(name = APPS_DATA_SOURCE)
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource appsDataSource() {
    return DataSourceBuilder.create().build();
}

@Primary
@Bean(name = APPS_ENTITY_MANAGER)
@PersistenceContext(unitName = APPS_PERSISTENCE_UNIT)
public LocalContainerEntityManagerFactoryBean appsEntityManagerFactory(EntityManagerFactoryBuilder builder,
        @Qualifier(APPS_DATA_SOURCE) DataSource appsDataSource) {
    LocalContainerEntityManagerFactoryBean emf = builder
            .dataSource(appsDataSource)
            .persistenceUnit(APPS_PERSISTENCE_UNIT)
            .packages(APPS_PACKAGES)
            .build();

    // Vendor adapter
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    emf.setJpaVendorAdapter(vendorAdapter);
    return emf;
}
}

辅助数据库:

@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = JpaGepeConfiguration.GEPE_ENTITY_MANAGER, 
    basePackages = { JpaGepeConfiguration.GEPE_REPOSITORIES })
@EnableAutoConfiguration
public class JpaGepeConfiguration {

public static final String GEPE_PERSISTENCE_UNIT = "gn1";
public static final String GEPE_DATA_SOURCE = "gn1ds";
public static final String GEPE_ENTITY_MANAGER = "gn1emf";
public static final String GEPE_TRANSACTION_MANAGER = "gn1tm";
public static final String GEPE_PACKAGES = ""; //my models package
public static final String GEPE_REPOSITORIES = ""; //my repos package

@Bean(name = GEPE_DATA_SOURCE)
@ConfigurationProperties(prefix = "gn1.datasource")
public DataSource gepeDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean(name = GEPE_ENTITY_MANAGER)
@PersistenceContext(unitName = GEPE_PERSISTENCE_UNIT)
public LocalContainerEntityManagerFactoryBean gepeEntityManagerFactory(EntityManagerFactoryBuilder builder,
        @Qualifier(GEPE_DATA_SOURCE) DataSource gepeDataSource) {
    LocalContainerEntityManagerFactoryBean emf = builder
            .dataSource(gepeDataSource)
            .persistenceUnit(GEPE_PERSISTENCE_UNIT)
            .packages(GEPE_PACKAGES)
            .build();

    // Vendor adapter
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    emf.setJpaVendorAdapter(vendorAdapter);
    return emf;
}
}

@Service类中用于主数据库的方法注释为:

@Transactional(transactionManager = JpaAppsConfiguration.APPS_TRANSACTION_MANAGER, readOnly = false, propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)

属性文件中的参数如下:

#DATABASE CONNECTION
#Primary
spring.datasource.username=
spring.datasource.password=
spring.datasource.driverClassName=
spring.datasource.url=j
spring.datasource.jdbcUrl=
spring.datasource.name=
#Secondary
gepe.datasource.username=
gepe.datasource.password=
gepe.datasource.driverClassName=
gepe.datasource.url=
gepe.datasource.jdbcUrl=
gepe.datasource.name=

希望这会有所帮助。

答案 1 :(得分:0)

这是批处理配置的代码片段。确保您还覆盖了 getTransactionManager 方法。 (将 qualfires 的值替换为您的值)

@Configuration
public class CustomBatchConfigurer extends DefaultBatchConfigurer {
    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource dataSource;

    @Autowired
    @Qualifier("primaryTransactionManager")
    private PlatformTransactionManager transactionManager;

    @Override
    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    @Override
    protected JobRepository createJobRepository() throws Exception {
        JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean();
        factoryBean.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");
        factoryBean.setDataSource(dataSource);
        factoryBean.setTransactionManager(transactionManager);

        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }
}