015 创建批次服务

本文介绍如何使用Spring Batch框架创建批处理作业,从CSV文件读取数据,转换数据并将结果写入数据库。通过示例代码展示了如何配置读取器、处理器和写入器,以及如何设置作业和步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文

https://spring.io/guides/gs/batch-processing/

直译

业务数据

通常,您的客户或业务分析师会提供电子表格。在这种情况下,你弥补了。

src/main/resources/sample-data.csv

Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe

此电子表格包含每行的名字和姓氏,以逗号分隔。正如您将看到的,这是Spring处理开箱即用的相当常见的模式。

接下来,编写SQL脚本以创建用于存储数据的表。

src/main/resources/schema-all.sql

DROP TABLE people IF EXISTS;

CREATE TABLE people  (
    person_id BIGINT IDENTITY NOT NULL PRIMARY KEY,
    first_name VARCHAR(20),
    last_name VARCHAR(20)
);

Spring Boot schema-@@platform@@.sql在启动期间自动运行。-all是所有平台的默认值。

创建一个业务类

现在您已经看到了数据输入和输出的格式,您可以编写代码来表示一行数据。

src/main/java/hello/Person.java

package hello;

public class Person {

    private String lastName;
    private String firstName;

    public Person() {
    }

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "firstName: " + firstName + ", lastName: " + lastName;
    }

}

您可以Person通过构造函数通过名字和姓氏实例化该类,也可以通过设置属性来实例化该类。

创建一个中间处理器

批处理中的一个常见范例是摄取数据,对其进行转换,然后将其传输到其他地方。在这里编写一个简单的转换器,将名称转换为大写。

src/main/java/hello/PersonItemProcessor.java

package hello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.batch.item.ItemProcessor;

public class PersonItemProcessor implements ItemProcessor<Person, Person> {

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

    @Override
    public Person process(final Person person) throws Exception {
        final String firstName = person.getFirstName().toUpperCase();
        final String lastName = person.getLastName().toUpperCase();

        final Person transformedPerson = new Person(firstName, lastName);

        log.info("Converting (" + person + ") into (" + transformedPerson + ")");

        return transformedPerson;
    }

}

PersonItemProcessor实现Spring Batch的ItemProcessor界面。这样可以轻松地将代码连接到您在本指南中进一步定义的批处理作业中。根据界面,您会收到一个传入的Person对象,然后将其转换为高端对象Person。

不要求输入和输出类型相同。实际上,在读取一个数据源之后,有时应用程序的数据流需要不同的数据类型。

整理批处理作业

现在您将实际的批处理作业放在一起。Spring Batch提供了许多实用程序类,可以减少编写自定义代码的需要。相反,您可以专注于业务逻辑。

src/main/java/hello/BatchConfiguration.java

package hello;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    // tag::readerwriterprocessor[]
    @Bean
    public FlatFileItemReader<Person> reader() {
        return new FlatFileItemReaderBuilder<Person>()
            .name("personItemReader")
            .resource(new ClassPathResource("sample-data.csv"))
            .delimited()
            .names(new String[]{"firstName", "lastName"})
            .fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
                setTargetType(Person.class);
            }})
            .build();
    }

    @Bean
    public PersonItemProcessor processor() {
        return new PersonItemProcessor();
    }

    @Bean
    public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder<Person>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
            .dataSource(dataSource)
            .build();
    }
    // end::readerwriterprocessor[]

    // tag::jobstep[]
    @Bean
    public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
        return jobBuilderFactory.get("importUserJob")
            .incrementer(new RunIdIncrementer())
            .listener(listener)
            .flow(step1)
            .end()
            .build();
    }

    @Bean
    public Step step1(JdbcBatchItemWriter<Person> writer) {
        return stepBuilderFactory.get("step1")
            .<Person, Person> chunk(10)
            .reader(reader())
            .processor(processor())
            .writer(writer)
            .build();
    }
    // end::jobstep[]
}

对于初学者来说,@EnableBatchProcessing注释添加了许多支持工作的关键bean,并为您节省了大量的工作量。此示例使用基于内存的数据库(由提供@EnableBatchProcessing),这意味着在完成后,数据将消失。

分解:

src/main/java/hello/BatchConfiguration.java

    @Bean
    public FlatFileItemReader<Person> reader() {
        return new FlatFileItemReaderBuilder<Person>()
            .name("personItemReader")
            .resource(new ClassPathResource("sample-data.csv"))
            .delimited()
            .names(new String[]{"firstName", "lastName"})
            .fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
                setTargetType(Person.class);
            }})
            .build();
    }

    @Bean
    public PersonItemProcessor processor() {
        return new PersonItemProcessor();
    }

    @Bean
    public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder<Person>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
            .dataSource(dataSource)
            .build();
    }
    ```
。第一块代码定义了输入,处理器和输出。- reader()创造一个ItemReader。它查找一个名为的文件sample-data.csv并使用足够的信息解析每个行项目以将其转换为Person。- processor()创建我们PersonItemProcessor之前定义的实例,用于大写数据。- write(DataSource)创造一个ItemWriter。这个目标是一个JDBC目标,并自动获取由其创建的dataSource的副本@EnableBatchProcessing。它包括插入Person由Java bean属性驱动的单个语句所需的SQL语句。

下一个块重点关注实际的作业配置。

src/main/java/hello/BatchConfiguration.java
@Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
    return jobBuilderFactory.get("importUserJob")
        .incrementer(new RunIdIncrementer())
        .listener(listener)
        .flow(step1)
        .end()
        .build();
}

@Bean
public Step step1(JdbcBatchItemWriter<Person> writer) {
    return stepBuilderFactory.get("step1")
        .<Person, Person> chunk(10)
        .reader(reader())
        .processor(processor())
        .writer(writer)
        .build();
}
```

。第一种方法定义作业,第二种方法定义单个步骤。作业是根据步骤构建的,其中每个步骤都涉及阅读器,处理器和编写器。

在此作业定义中,您需要增量器,因为作业使用数据库来维护执行状态。然后列出每个步骤,此作业只有一个步骤。作业结束,Java API生成完美配置的作业。

在步骤定义中,您可以定义一次写入的数据量。在这种情况下,它一次最多可写入十条记录。接下来,使用之前注入的位配置读取器,处理器和写入器。

chunk()是前缀的,<Person,Person>因为它是一个通用的方法。这代表了输入和输出类型的各处理的“块”,并且线与ItemReader和ItemWriter。
src/main/java/hello/JobCompletionNotificationListener.java

package hello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {

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

	private final JdbcTemplate jdbcTemplate;

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

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

			jdbcTemplate.query("SELECT first_name, last_name FROM people",
				(rs, row) -> new Person(
					rs.getString(1),
					rs.getString(2))
			).forEach(person -> log.info("Found <" + person + "> in the database."));
		}
	}
}

此代码侦听作业的时间BatchStatus.COMPLETED,然后用于JdbcTemplate检查结果。

使应用程序可执行

虽然批处理可以嵌入到Web应用程序和WAR文件中,但下面演示的更简单的方法创建了一个独立的应用程序。您将所有内容打包在一个可执行的JAR文件中,由一个好的旧Java main()方法驱动。

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

@SpringBootApplication 是一个便利注释,添加了以下所有内容:

  • @Configuration 标记该类作为应用程序上下文的bean定义的来源。

  • @EnableAutoConfiguration 告诉Spring Boot开始根据类路径设置,其他bean和各种属性设置添加bean。

  • 通常你会添加@EnableWebMvc一个Spring MVC应用程序,但Spring Boot会在类路径上看到spring-webmvc时自动添加它。这会将应用程序标记为Web应用程序并激活关键行为,例如设置a DispatcherServlet。

  • @ComponentScan告诉Spring在包中寻找其他组件,配置和服务hello,允许它找到控制器。

该main()方法使用Spring Boot的SpringApplication.run()方法启动应用程序。您是否注意到没有一行XML?也没有web.xml文件。此Web应用程序是100%纯Java,您无需处理配置任何管道或基础结构。

出于演示目的,可以使用代码创建JdbcTemplate,查询数据库,并打印批处理作业插入的人员的名称。

构建可执行的JAR

您可以使用Gradle或Maven从命令行运行该应用程序。或者,您可以构建一个包含所有必需依赖项,类和资源的可执行JAR文件,并运行该文件。这使得在整个开发生命周期中,跨不同环境等将服务作为应用程序发布,版本和部署变得容易。

如果您使用的是Gradle,则可以使用./gradlew bootRun。或者您可以使用构建JAR文件./gradlew build。然后你可以运行JAR文件:

java -jar build / libs / gs-batch-processing-0.1.0.jar

如果您使用的是Maven,则可以使用该应用程序运行该应用程序./mvnw spring-boot:run。或者您可以使用构建JAR文件./mvnw clean package。然后你可以运行JAR文件:

java -jar target / gs-batch-processing-0.1.0.jar

上面的过程将创建一个可运行的JAR。您也可以选择构建经典WAR文件。
该作业为每个被转化的人打印出一条线。作业运行后,您还可以查看查询数据库的输出。

Converting (firstName: Jill, lastName: Doe) into (firstName: JILL, lastName: DOE)
Converting (firstName: Joe, lastName: Doe) into (firstName: JOE, lastName: DOE)
Converting (firstName: Justin, lastName: Doe) into (firstName: JUSTIN, lastName: DOE)
Converting (firstName: Jane, lastName: Doe) into (firstName: JANE, lastName: DOE)
Converting (firstName: John, lastName: Doe) into (firstName: JOHN, lastName: DOE)
Found <firstName: JILL, lastName: DOE> in the database.
Found <firstName: JOE, lastName: DOE> in the database.
Found <firstName: JUSTIN, lastName: DOE> in the database.
Found <firstName: JANE, lastName: DOE> in the database.
Found <firstName: JOHN, lastName: DOE> in the database.

摘要

恭喜!您构建了一个批处理作业,从电子表格中提取数据,对其进行处理并将其写入数据库。

扩展知识

<think>好的,用户问的是MSC3N如何创建批次。首先,我需要确定MSC3N是什么系统或工具。通常,像MSC3N这样的缩写可能与SAP系统相关,因为在SAP中,批次管理是一个常见的功能,事务代码如MSC2N用于批次创建,而MSC3N用于批次查看。用户可能混淆了事务代码,或者他们使用的系统有自定义的事务代码MSC3N来创建批次。 接下来,我需要考虑标准的SAP批次创建流程。通常,在SAP中创建批次是通过事务代码MSC1N或MSC2N。MSC3N通常是用来显示批次主数据的。所以用户可能在询问如何创建批次,但错误地引用了MSC3N。或者他们的系统可能对事务代码进行了定制,将批次创建功能分配给了MSC3N。 需要明确的是,用户是否确定事务代码正确。如果他们确实需要使用MSC3N来创建批次,可能需要检查系统配置,或者是否有特定的步骤或权限要求。此外,可能涉及物料主数据是否启用了批次管理,以及工厂和库存地点是否配置正确。 如果用户的环境确实使用MSC3N来创建批次,可能需要指导他们进入该事务代码,输入物料、工厂等信息,然后保存。但通常需要确认系统的自定义设置,因为标准SAP中MSC3N不是用于创建的。 可能还需要考虑用户是否有权限问题,或者是否需要通过其他路径访问批次创建功能。例如,在某些情况下,可能需要通过物料主数据或生产订单触发批次的自动创建。 总结下来,回答应该先确认事务代码的正确性,然后分步骤说明标准流程,并建议用户检查系统定制或咨询管理员,以防他们的环境有特殊配置。同时提醒批次管理的前提条件,如物料主数据的批次视图维护等。</think>在SAP系统中,若需创建批次(Batch),通常使用事务代码 **MSC1N** 或 **MSC2N**。但根据您提到的 **MSC3N**(通常用于显示批次主数据),可能存在以下两种可能性: --- ### 1. **确认事务代码用途** - **MSC3N** 在标准SAP中用于 **查看批次主数据**,而非创建批次。 - 若需创建批次,请尝试以下操作: - **事务代码 MSC1N**:手动创建批次。 - **事务代码 MSC2N**:通过物料凭证(如生产订单、采购订单)自动生成批次。 --- ### 2. **通过MSC1N手动创建批次的步骤** - **步骤1**:输入事务代码 **MSC1N**,按回车。 - **步骤2**:填写必填字段: - **物料号**:需启用批次管理的物料。 - **工厂**:关联的工厂代码。 - **批次**:可手动输入批次号(若系统未自动生成)。 - **步骤3**:维护批次特性(如生产日期、有效期、供应商等)。 - **步骤4**:保存(按 **Ctrl+S** 或点击保存按钮),系统生成批次号。 --- ### 3. **通过MSC2N自动生成批次** - **适用场景**:通过采购订单、生产订单等自动触发批次创建。 - **步骤1**:执行事务代码 **MSC2N**。 - **步骤2**:输入物料凭证(如采购订单号)并回车。 - **步骤3**:系统自动分配批次号,补充批次特性后保存。 --- ### 4. **特殊情况说明** - 若您确认需通过 **MSC3N** 创建批次,可能是系统定制化配置导致: 1. 尝试在MSC3N界面查找“新建”或“创建”按钮。 2. 联系系统管理员确认权限及事务代码功能是否被修改。 --- ### 5. **前提条件** - 物料主数据的 **批次视图** 需已维护(事务代码 **MM02**)。 - 工厂层级的批次管理配置需启用(通过事务代码 **OMJJ**)。 --- ### 总结 建议优先使用 **MSC1N** 或 **MSC2N** 创建批次。若系统中 **MSC3N** 被定制为创建功能,需结合具体配置操作。如仍有疑问,可提供更多上下文进一步分析!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值