Spring Batch:创建作业时出现不满足的依赖异常

问题描述 投票:0回答:1

我是 Spring Batch 的新手,非常感谢您帮助找到下面列出的异常原因,我已经花了相当多的时间来调试异常。 我正在按照以下步骤进行 Spring Batch 培训: - 基于此链接“https://spring.io/guides/gs/batch-processing#header”中的 spring 教程创建批处理服务

  • 按照教程中所述使用内存数据库 H2 实现并运行,它按预期工作。

    • 我升级了批处理服务,使用 MySQL 数据库作为作业存储库,使用 PostgreSQL 作为数据存储库,用于培训目的。
      • 尝试创建 Job bean 时,它在运行时失败,抛出“UnsatisfiedDependencyException”,如下面的日志所示。
I expect the Spring Batch Service continue working after upgrade to use the MySQL and PostgreSQL databases
Debug Log:
JRE Oracle Corporation/22 is not supported, advanced source lookup disabled.
 Eclipse debugger will use less precise source lookup implementation for this debug session, but everything else will continue to work otherwise.
Upgrading Eclipse to the latest version will likely make this warning go away.
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v3.2.10)

2024-10-31T11:18:59.431-04:00  INFO 14844 --- [           main] c.e.b.BatchProcessingApplication         : Starting BatchProcessingApplication using Java 22 with PID 14844 (D:\User\Gilmar\git-repo\spring-batch-mastery\spring-batch-initial\target\classes started by Gilmar in D:\User\Gilmar\git-repo\spring-batch-mastery\spring-batch-initial)
2024-10-31T11:18:59.434-04:00  INFO 14844 --- [           main] c.e.b.BatchProcessingApplication         : No active profile set, falling back to 1 default profile: "default"
2024-10-31T11:19:00.176-04:00  WARN 14844 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'batchConfiguration' of type [com.example.batchprocessing.BatchConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [jobRegistryBeanPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.
2024-10-31T11:19:00.204-04:00  WARN 14844 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'jobRegistry' of type [org.springframework.batch.core.configuration.support.MapJobRegistry] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-10-31T11:19:00.212-04:00  INFO 14844 --- [           main] c.e.b.BatchProcessingApplication         : MyApplication::run()
2024-10-31T11:19:00.283-04:00  INFO 14844 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-10-31T11:19:00.753-04:00  INFO 14844 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@67064bdc
2024-10-31T11:19:00.756-04:00  INFO 14844 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-10-31T11:19:00.775-04:00  INFO 14844 --- [           main] c.e.batchprocessing.BatchConfiguration   : ******jobTemplate running******
2024-10-31T11:19:00.788-04:00  INFO 14844 --- [           main] c.example.batchprocessing.MyComponent    : MyComponent::run()
2024-10-31T11:19:00.790-04:00  INFO 14844 --- [           main] c.e.batchprocessing.BatchConfiguration   : ******FlatFileItemReader******
2024-10-31T11:19:00.804-04:00  INFO 14844 --- [           main] c.e.batchprocessing.BatchConfiguration   : ******PersonItemProcessor******
2024-10-31T11:19:00.828-04:00  INFO 14844 --- [           main] c.e.batchprocessing.BatchConfiguration   : ******JdbcBatchItemWriter******
2024-10-31T11:19:00.841-04:00  WARN 14844 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'importUserJob' defined in class path resource [com/example/batchprocessing/BatchConfiguration.class]: Unsatisfied dependency expressed through method 'importUserJob' parameter 0: No qualifying bean of type 'org.springframework.batch.core.repository.JobRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("mySQLDataSource")}
2024-10-31T11:19:00.842-04:00  INFO 14844 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-10-31T11:19:00.848-04:00  INFO 14844 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2024-10-31T11:19:00.857-04:00  INFO 14844 --- [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-10-31T11:19:00.896-04:00 ERROR 14844 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'importUserJob' defined in class path resource [com/example/batchprocessing/BatchConfiguration.class]: Unsatisfied dependency expressed through method 'importUserJob' parameter 0: No qualifying bean of type 'org.springframework.batch.core.repository.JobRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("mySQLDataSource")}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) ~[spring-context-6.1.13.jar:6.1.13]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.13.jar:6.1.13]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.10.jar:3.2.10]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.10.jar:3.2.10]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.2.10.jar:3.2.10]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.2.10.jar:3.2.10]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.2.10.jar:3.2.10]
    at com.example.batchprocessing.BatchProcessingApplication.main(BatchProcessingApplication.java:17) ~[classes/:na]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.batch.core.repository.JobRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("mySQLDataSource")}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1880) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1406) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.13.jar:6.1.13]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.13.jar:6.1.13]
    ... 18 common frames omitte

------代码------

@SpringBootApplication
public class BatchProcessingApplication {

    public static void main(String[] args) {            System.exit(SpringApplication.exit(SpringApplication.run(BatchProcessingApplication.class, args)));
    }
}
@Configuration
@PropertySource("classpath:application.properties")
public class BatchConfiguration extends DefaultBatchConfiguration {
    
    private static final Logger log = LoggerFactory.getLogger(BatchConfiguration.class);
    
    @Bean("jdbcTemplate")
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        log.info("******jobTemplate running******");
        return new JdbcTemplate(dataSource);
    }
    
    @Bean("reader")
    public FlatFileItemReader<Person> reader() {
        log.info("******FlatFileItemReader******");
      return new FlatFileItemReaderBuilder<Person>()
        .name("personItemReader")
        .resource(new ClassPathResource("sample-data.csv"))
        .delimited()
        .delimiter(",")
        .names("firstName", "lastName")
        .targetType(Person.class)
        .build();
    }
    @Bean("processor")
    public PersonItemProcessor processor() {
      log.info("******PersonItemProcessor******");
      return new PersonItemProcessor();
    }

    @Bean("writer")
    public JdbcBatchItemWriter<Person> writer(@Qualifier("pgDataRepository") DataSource dataRepository) {
        log.info("******JdbcBatchItemWriter******");
      return new JdbcBatchItemWriterBuilder<Person>()
        .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
        .dataSource(dataRepository)
        .beanMapped()
        .build();
    }   
    @Bean("importUserJob")
    public Job importUserJob(@Qualifier("mySQLDataSource")JobRepository jobRepository,Step step1, JobCompletionNotificationListener listener) {
        log.info("******importUserJob******");
        return new JobBuilder("importUserJob", jobRepository)
            .listener(listener)
            .start(step1)
            .build();
    }
    @Bean("step1")
    public Step step1(JobRepository jobRepository,  DataSourceTransactionManager transactionManager,
            @Qualifier("reader")FlatFileItemReader<Person> reader, @Qualifier("processor")PersonItemProcessor processor, @Qualifier("writer")JdbcBatchItemWriter<Person> writer) {
        
        log.info("******step1******");
        return new StepBuilder("step1", jobRepository)
        .<Person, Person> chunk(3, transactionManager)
        .reader(reader)
        .processor(processor)
        .writer(writer)
        .build();
    }
}
@Component
public class JobCompletionNotificationListener implements JobExecutionListener {

    private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);

    private final JdbcTemplate jdbcTemplate;

    public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
          }

    @Override
    public void beforeJob(JobExecution jobExecution) {
        JobExecutionListener.super.beforeJob(jobExecution);
        log.info("Job {} started", jobExecution.getJobInstance().getJobName());
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        log.info("Job {} end", jobExecution.getJobInstance().getJobName());
        if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
            log.info("!!! JOB FINISHED! Time to verify the results");

            jdbcTemplate.query("SELECT first_name, last_name FROM people", new DataClassRowMapper<>(Person.class))
                    .forEach(person -> log.info("Found <{{}}> in the database.", person));
        }
    }
}
public record Person(String firstName, String lastName) {}
public class PersonItemProcessor implements ItemProcessor<Person, Person> {

    private static final Logger log = LoggerFactory.getLogger(PersonItemProcessor.class);
    
    @Override
    public Person process(final Person person) {
        final String firstName = person.firstName().toUpperCase();
        final String lastName = person.lastName().toUpperCase();
        
        final Person transformedPerson = new Person(firstName, lastName);
        
        log.info("Converting (" + person + ") into (" + transformedPerson + ")");

        return transformedPerson;
    }
}

src/主/资源

***application.properties***

db.job.repo.driverClassName=com.mysql.cj.jdbc.Driver
db.job.repo.jdbc-url=jdbc:mysql://localhost:3306/batch-initial?currentSchema=batch-initial
db.job.repo.username=root
db.job.repo.password=Gl98nd02!
db.job.repo.schema=batch-initial
# Whether to populate schema for Spring Batch in case it's absent
#batch.db.initialize-schema=ALWAYS
db.job.repo.initialize-schema=ALWAYS

db.src.jdbc-url=jdbc:postgresql://127.0.0.1:5432/SPRINGBATCH?currentSchema=batch-initial
db.src.username=PGTRAINING
db.src.password=secret01!
db.src.driverClassName=org.postgresql.Driver
***data.sql***
DROP TABLE people IF EXISTS;

CREATE TABLE people  (
 --   id BIGINT AUTO_INCREMENT PRIMARY KEY,
    person_id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY,
    first_name VARCHAR(20),
    last_name VARCHAR(20)
);
---------------------------------------------------------------------------------------------------
***sample-data.txt***
Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe

POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>batchprocessing</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Batch Service</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>22</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
                <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
spring spring-boot spring-batch multiple-databases
1个回答
0
投票

这可能是由于作业存储库切换到 MySQL 后 Spring Batch 应用程序的配置所致。

确保您已在配置中为 MySQL 数据库定义了 DataSource bean。 Spring Batch 需要一个 DataSource bean 来创建 JobRepository

示例:

@Bean
@Primary // Ensure this is the primary DataSource
public DataSource mySQLDataSource() {
    return DataSourceBuilder.create()
            .driverClassName("com.mysql.cj.jdbc.Driver")
            .url("jdbc:mysql://localhost:3306/batch-initial?currentSchema=batch-initial")
            .username("root")
            .password("Gl98nd02!")
            .build();
}

确保您定义了 JobRepository bean。你可以这样定义它:

@Bean
public JobRepository jobRepository(DataSource mySQLDataSource) throws Exception {
    return new JobRepositoryFactoryBean()
            .setDataSource(mySQLDataSource)
            .setTransactionManager(new DataSourceTransactionManager(mySQLDataSource))
            .setIsolationLevelForCreate("ISOLATION_SERIALIZABLE")
            .setMaxVarCharLength(255)
            .afterPropertiesSet();
}
© www.soinside.com 2019 - 2024. All rights reserved.