spring-data-jpa 为什么在使用RepositoryItemReader时,数据源会干扰Spring Batch?

whlutmcx  于 2022-11-10  发布在  Spring
关注(0)|答案(2)|浏览(188)

我正在尝试使用Spring Batch在Postgres数据库和MongoDB之间迁移一些数据。我配置了一个非常简单的ItemReaderItemProcessorItemWriter,并且一切都按预期运行。但是,如果我切换到RepositoryItemReader,我会收到以下错误:

java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@684430c1] for key [HikariDataSource (HikariPool-1)] bound to thread

如果我理解正确的话,EntityManager或TransactionManager有问题,但我不知道是什么问题,也不知道为什么它与一个不与存储库一起工作的简单ItemReader一起工作,但它使用的是相同的数据源。
我将非常感激任何帮助。

以下是我的源数据库配置:

package com.example.batch.primary;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    entityManagerFactoryRef = "primaryEntityManagerFactory",
    transactionManagerRef = "primaryTransactionManager",
    basePackages = {"com.example.batch.primary"}
    )
public class PrimaryDBConfig {

@Bean(name = "primaryDataSource")
@Primary
public DataSource primaryDatasource(){

    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create()
                     .driverClassName("org.postgresql.Driver")
                     .url("jdbc:postgresql://localhost:5432/postgres")
                     .username("test")
                     .password("test");
    return dataSourceBuilder.build();
}

@Bean(name = "primaryEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
                                                                          @Qualifier("primaryDataSource")
                                                                          DataSource primaryDataSource){
    return builder.dataSource(primaryDataSource)
                  .packages("com.example.batch.primary")
                  .build();
}

@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
        @Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory)
{
    return new JpaTransactionManager(primaryEntityManagerFactory);
}
}

以下是MongoDB的配置:

package com.example.batch.secondary;

@EnableMongoRepositories(basePackages = "com.example.batch.secondary")
@Configuration
public class MongoDBConfig {

@Bean
public MongoClient mongo() {
    ConnectionString connectionString = new ConnectionString("mongodb+srv://mongoadmin:blablabla.mongodb.net/?retryWrites=true&w=majority");
    MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
            .applyConnectionString(connectionString)
            .build();

    return MongoClients.create(mongoClientSettings);
}

@Bean
public MongoTemplate mongoTemplate() throws Exception {
    return new MongoTemplate(mongo(), "test");
}
}

以下是储存库项目读取器:

package com.example.batch.stepcomponents;

@Component
public class RepositoryReader extends RepositoryItemReader<Partner> {

public RepositoryReader(@Autowired PartnerRepository partnerRepository){
    setRepository(partnerRepository);
    setPageSize(1);
    setSort(Map.of("id", Sort.Direction.ASC));
    setMethodName("findAll");
}
}

批配置:

@Configuration
@EnableBatchProcessing
public class BatchConfig {

@Autowired
public JobBuilderFactory jobBuilderFactory;

@Autowired
public StepBuilderFactory stepBuilderFactory;

@Autowired
RepositoryReader repositoryReader;

@Autowired
CustomWriter customWriter;

@Autowired
CustomProcessor customProcessor;

@Bean
public Job createJob() {
    return jobBuilderFactory.get("MyJob")
            .incrementer(new RunIdIncrementer())
            .flow(createStep())
            .end()
            .build();
}

@Bean
public Step createStep() {

    return stepBuilderFactory.get("MyStep")
            .<Partner, Student> chunk(1)
            .reader(repositoryReader)
            .processor(customProcessor)
            .writer(customWriter)
            .build();
}
}
hpcdzsge

hpcdzsge1#

所以我试着取出EntityManagerFactory和TransactionManager,现在它工作了。我猜它们在启动服务器时已经自动初始化了。
是的,默认情况下,如果您提供了一个DataSource bean,Spring Batch将使用一个DataSourceTransactionManager,而不是您所期望的JPA bean。

The transaction manager provided by this annotation will be of type:
 * ResourcelessTransactionManager if no DataSource is provided within the context
 * DataSourceTransactionManager if a DataSource is provided within the context

为了使用JPA事务管理器,您需要配置一个自定义的BatchConfigurer并覆盖getTransactionManager,如下所示:

@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource, EntityManagerFactory entityManagerFactory) {
    return new DefaultBatchConfigurer(dataSource) {
        @Override
        public PlatformTransactionManager getTransactionManager() {
            return new JpaTransactionManager(entityManagerFactory);
        }
    };
}

请注意,从v5开始将不再需要此功能,请参阅:

您还可以在步骤上设置JPA事务管理器:

@Bean
public Step createStep(JpaTransactionManager jpaTransactionManager) {

    return stepBuilderFactory.get("MyStep")
            .<Partner, Student> chunk(1)
            .reader(repositoryReader)
            .processor(customProcessor)
            .writer(customWriter)
            .transactionManager(jpaTransactionManager)
            .build();
}
tv6aics1

tv6aics12#

如果未定义其他TransactionManager,则将“spring-data-jpa”添加为依赖项将自动配置JpaTransactionManager

相关问题