Как выполнить пакетное задание Spring, которое может считывать данные из базы данных через регулярные промежутки времени

Я пытаюсь реализовать пакетное приложение Spring, которое может читать из двух таблиц в базе данных mhysql (используя запрос соединения) и печатать выходные данные на консоли, и которое можно запланировать для запуска с интервалом, скажем, в 10 секунд. Хотя мое приложение работает без каких-либо ошибок и также печатает шаги в консоли, но не печатает никаких данных из базы данных.

Ниже приведен мой файл 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.5</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.rcg.m360</groupId>
    <artifactId>crm-scheduler</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>m360-crm-scheduler</name>
    <description>Project for the scheduler to get individual ids from Manifest
        DB</description>
    <properties>
        <java.version>17</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-data-jpa</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--
        https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>3.2.5</version>
            <scope>test</scope>
        </dependency>
        <!--
        https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>            
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Ниже приведен мой файл application.yml:

    spring:
  application:
    name: m360-crm-scheduler
  batch:
    job:
      enabled: true
  jpa:
    database-platform: org.hibernate.dialect.MySQLDialect
    show-sql: true
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect
        format_sql: true
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${MANIFEST_DB_URL}
    username: ${MANIFEST_DB_USERNAME}
    password: ${MANIFEST_DB_PASSWORD}
    initialize: true

server:
  port: 8081

Вот Java-класс BatchConfiguration:

@Configuration
@EnableBatchProcessing
@EnableScheduling
public class BatchConfiguration {
    
    @Autowired
    private JobRepository jobRepository;
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Autowired
    DataSourceConfig dataSourceConfig;
    
    private static final Logger logger = LoggerFactory.getLogger(BatchConfiguration.class);
    
    @Bean
    public Job crmSchedulerJob() {
        logger.info("Job execution started...");
        return new JobBuilder("crmSchedulerJob", jobRepository)
                .start(crmSchedulerStep())
                .build();
    }
    
    @Bean
    public Step crmSchedulerStep() {
        return new StepBuilder("crmSchedulerStep", jobRepository)
                .<SchedulerData, SchedulerData>chunk(10, transactionManager)
                .reader(crmSchedulerReader())                
                .writer(crmSchedulerWriter())
                .build();
    }
    
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(dataSourceConfig.getDriverClassName());
        dataSource.setUrl(dataSourceConfig.getUrl());
        dataSource.setUsername(dataSourceConfig.getUserName());
        dataSource.setPassword(dataSourceConfig.getPassword());
        return dataSource;
    }
    
    @Bean
    public JdbcCursorItemReader<SchedulerData> crmSchedulerReader() {
        String query = "select gRes.Individual_id, res.Ship_code, res.is_delete from Guest_reservation gRes join Reservation res \r\n"
                + "on gRes.Super_pnr_no=res.Super_pnr_no where res.Sailing_startdate>=DATE_SUB(CURDATE(), \r\n"
                + "INTERVAL 21 DAY)";
        logger.info("The query is: "+query);
        LocalDate date = LocalDate.now().minusDays(21);
        
        return new JdbcCursorItemReaderBuilder<SchedulerData>()
                .dataSource(getDataSource())
                .name("crmSchedulerReader")
                .sql(query)
                .beanRowMapper(SchedulerData.class)                
                .build();
    }    
    
    @Bean
    public ItemWriter<SchedulerData> crmSchedulerWriter() {
        logger.info("Start of writer method");
        return items -> {
            for (SchedulerData item : items) {
                logger.info("Writing item: " + item);
            }
        };
    }
    
    @Scheduled(fixedRate = 10000)
    public void perform() throws Exception {
        logger.info("Start of perform method");
        crmSchedulerJob();
    }
}

Вот основной класс приложения:

@SpringBootApplication
@EnableScheduling
public class M360CrmSchedulerApplication {

    public static void main(String[] args) {
        SpringApplication.run(M360CrmSchedulerApplication.class, args);
    }
}

Вот класс DataSourceConfig —

@Data
@Component
public class DataSourceConfig {
    
    @Value("${spring.datasource.url}")
    private String url;
    
    @Value("${spring.datasource.username}")
    private String userName;
    
    @Value("${spring.datasource.password}")
    private String password;
    
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
}

Вот класс DTO SchedulerData:

public class SchedulerData {
    
    private int individualId;
    private String shipCode;
    private String validityDateTo;
    public SchedulerData(){
        super();
    }
    public SchedulerData(int individualId, String shipCode, String validityDateTo) {
        this.individualId = individualId;
        this.shipCode = shipCode;
        this.validityDateTo = validityDateTo;
    }
    
    public String getValidityDateTo() {
        return validityDateTo;
    }
    
    public void setValidityDateTo(String validityDateTo) {
        this.validityDateTo = validityDateTo;
    }
    
    public int getIndividualId() {
        return individualId;
    }
    
    public void setIndividualId(int individualId) {
        this.individualId = individualId;
    }
    
    public String getShipCode() {
        return shipCode;
    }
    
    public void setShipCode(String shipCode) {
        this.shipCode = shipCode;
    }
}

Я указал детали подключения к базе данных в конфигурациях запуска. Я получаю следующий вывод в консоли:

Started M360CrmSchedulerApplication in 3.549 seconds (process running for 4.115)
Start of perform method
Start of perform method
Start of perform method

Из журнала мы видим, что метод выполнения класса BatchConfiguration выполняется каждые 10 секунд, но метод задания не выполняется, поскольку оператор журнала в методе задания не печатается. Я не могу понять, почему управление не переходит к методу задания, любая помощь по этому вопросу будет высоко оценена.

0
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем коде метод выполнения фактически не запускает выполнение пакетного задания Spring. Вместо этого он просто вызывает метод, определяющий задание (crmSchedulerJob), без его фактического запуска.

Если вы адаптируете свой метод выполнения, как показано ниже, он должен работать.

@Autowired
private JobLauncher jobLauncher;
.
.
@Scheduled(fixedRate = 10000)
public void perform() throws Exception {
    logger.info("Start of perform method");
    JobParameters jobParameters = new JobParametersBuilder()
            .addLong("time", System.currentTimeMillis())
            .toJobParameters();

    JobExecution jobExecution = jobLauncher.run(crmSchedulerJob(), jobParameters);
    logger.info("Job Execution Status: " + jobExecution.getStatus());
}

Примечание. Если вы не добавляли таблицу для пакетного задания Spring, создайте ее, выполнив этот запрос

Я внес предложенные вами изменения, но после его запуска возникла следующая ошибка: Запущено M360CrmSchedulerApplication через 3,542 секунды (процесс выполняется для 4,05) 2024-06-18T10:00:32.927+05:30 ОШИБКА 20084 --- [m360-crm- планировщик] [scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler: в запланированном задании произошла непредвиденная ошибка org.springframework.jdbc.BadSqlGrammarException: ReadyStatementCallback; плохая грамматика SQL [SELECT JOB_INSTANCE_ID, JOB_NAME FROM BATCH_JOB_INSTANCE WHERE JOB_NAME = ? и JOB_KEY = ?]

Debabrata Mukherjee 18.06.2024 06:36

И это несмотря на то, что тот же запрос на соединение выполняется в рабочей среде MySQL и показывает результаты.

Debabrata Mukherjee 18.06.2024 06:50

Это решено после ручного выполнения запросов для создания таблиц метаданных пакета Spring отсюда — github.com/spring-projects/spring-batch/blob/main/…

Debabrata Mukherjee 18.06.2024 07:20

да, вам нужно добавить эти таблицы :) и, пожалуйста, примите ответ, если это помогло

Abhishek Kotalwar 18.06.2024 07:29

Другие вопросы по теме