Wprowadzenie do Spring Batch

1. Wstęp

W tym artykule skupimy się na praktycznym, skoncentrowanym na kodzie wprowadzeniu do Spring Batch. Spring Batch to platforma przetwarzania przeznaczona do wydajnego wykonywania zadań.

Jest to aktualna wersja 3.0, która obsługuje Spring 4 i Java 8. Obsługuje również JSR-352, nową specyfikację java do przetwarzania wsadowego.

Oto kilka interesujących i praktycznych przykładów użycia frameworka.

2. Podstawy przepływu pracy

Spring batch jest zgodny z tradycyjną architekturą wsadową, w której repozytorium zadań wykonuje prace związane z planowaniem i interakcją z zadaniem.

Zadanie może składać się z więcej niż jednego kroku - a każdy krok zwykle następuje po sekwencji odczytywania danych, przetwarzania ich i zapisywania.

I oczywiście framework wykona dla nas większość ciężkiej pracy - szczególnie jeśli chodzi o niskopoziomową wytrwałość pracy z zadaniami - używając sqlite do repozytorium zadań.

2.1. Nasz przykład zastosowania

Prosty przypadek użycia, którym się tutaj zajmiemy, polega na tym, że dokonamy migracji niektórych danych transakcji finansowych z CSV do XML.

Plik wejściowy ma bardzo prostą strukturę - zawiera transakcję w wierszu, na którą składają się: nazwa użytkownika, identyfikator użytkownika, data transakcji oraz kwota:

username, userid, transaction_date, transaction_amount devendra, 1234, 31/10/2015, 10000 john, 2134, 3/12/2015, 12321 robin, 2134, 2/02/2015, 23411

3. Maven POM

Zależności wymagane w tym projekcie to: rdzeń sprężyny, partia sprężyny i złącze sqlite jdbc:

   org.xerial sqlite-jdbc 3.15.1   org.springframework spring-oxm 5.2.0.RELEASE   org.springframework spring-jdbc 5.2.0.RELEASE   org.springframework.batch spring-batch-core 4.2.0.RELEASE 

4. Spring Batch Config

Pierwszą rzeczą, którą zrobimy, jest skonfigurowanie Spring Batch za pomocą XML:

Oczywiście dostępna jest również konfiguracja Java:

@Configuration @EnableBatchProcessing public class SpringConfig { @Value("org/springframework/batch/core/schema-drop-sqlite.sql") private Resource dropReopsitoryTables; @Value("org/springframework/batch/core/schema-sqlite.sql") private Resource dataReopsitorySchema; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.sqlite.JDBC"); dataSource.setUrl("jdbc:sqlite:repository.sqlite"); return dataSource; } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws MalformedURLException { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); databasePopulator.addScript(dropReopsitoryTables); databasePopulator.addScript(dataReopsitorySchema); databasePopulator.setIgnoreFailedDrops(true); DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator); return initializer; } private JobRepository getJobRepository() throws Exception { JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); factory.setDataSource(dataSource()); factory.setTransactionManager(getTransactionManager()); factory.afterPropertiesSet(); return (JobRepository) factory.getObject(); } private PlatformTransactionManager getTransactionManager() { return new ResourcelessTransactionManager(); } public JobLauncher getJobLauncher() throws Exception { SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); jobLauncher.setJobRepository(getJobRepository()); jobLauncher.afterPropertiesSet(); return jobLauncher; } }

5. Spring Batch Job Config

Napiszmy teraz nasz opis pracy dla pracy z CSV do XML:

                           com.baeldung.spring_batch_intro.model.Transaction           

I oczywiście podobna konfiguracja zadań oparta na Javie:

public class SpringBatchConfig { @Autowired private JobBuilderFactory jobs; @Autowired private StepBuilderFactory steps; @Value("input/record.csv") private Resource inputCsv; @Value("file:xml/output.xml") private Resource outputXml; @Bean public ItemReader itemReader() throws UnexpectedInputException, ParseException { FlatFileItemReader reader = new FlatFileItemReader(); DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); String[] tokens = { "username", "userid", "transactiondate", "amount" }; tokenizer.setNames(tokens); reader.setResource(inputCsv); DefaultLineMapper lineMapper = new DefaultLineMapper(); lineMapper.setLineTokenizer(tokenizer); lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); reader.setLineMapper(lineMapper); return reader; } @Bean public ItemProcessor itemProcessor() { return new CustomItemProcessor(); } @Bean public ItemWriter itemWriter(Marshaller marshaller) throws MalformedURLException { StaxEventItemWriter itemWriter = new StaxEventItemWriter(); itemWriter.setMarshaller(marshaller); itemWriter.setRootTagName("transactionRecord"); itemWriter.setResource(outputXml); return itemWriter; } @Bean public Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setClassesToBeBound(new Class[] { Transaction.class }); return marshaller; } @Bean protected Step step1(ItemReader reader, ItemProcessor processor, ItemWriter writer) { return steps.get("step1"). chunk(10) .reader(reader).processor(processor).writer(writer).build(); } @Bean(name = "firstBatchJob") public Job job(@Qualifier("step1") Step step1) { return jobs.get("firstBatchJob").start(step1).build(); } }

OK, więc teraz, gdy mamy całą konfigurację, podzielmy ją i zacznijmy omawiać.

5.1. Czytaj dane i twórz obiekty za pomocą ItemReader

Najpierw skonfigurowaliśmy cvsFileItemReader, który odczyta dane z pliku record.csv i przekształci je w obiekt Transaction :

@SuppressWarnings("restriction") @XmlRootElement(name = "transactionRecord") public class Transaction { private String username; private int userId; private LocalDateTime transactionDate; private double amount; /* getters and setters for the attributes */ @Override public String toString() { return "Transaction [username=" + username + ", userId=" + userId + ", transactionDate=" + transactionDate + ", amount=" + amount + "]"; } }

Aby to zrobić - używa niestandardowego mapera:

public class RecordFieldSetMapper implements FieldSetMapper { public Transaction mapFieldSet(FieldSet fieldSet) throws BindException { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyy"); Transaction transaction = new Transaction(); transaction.setUsername(fieldSet.readString("username")); transaction.setUserId(fieldSet.readInt(1)); transaction.setAmount(fieldSet.readDouble(3)); String dateString = fieldSet.readString(2); transaction.setTransactionDate(LocalDate.parse(dateString, formatter).atStartOfDay()); return transaction; } }

5.2. Przetwarzanie danych za pomocą ItemProcessor

Stworzyliśmy własny procesor elementów CustomItemProcessor . To nie przetwarza niczego związanego z obiektem transakcji - wszystko, co robi, to przekazuje oryginalny obiekt pochodzący od czytelnika do pisarza:

public class CustomItemProcessor implements ItemProcessor { public Transaction process(Transaction item) { return item; } }

5.3. Zapisywanie obiektów do FS za pomocą ItemWriter

Na koniec zapiszemytransakcję w pliku xml znajdującym się w xml / output.xml :

5.4. Konfiguracja zadania wsadowego

Więc wszystko, co musimy zrobić, to połączyć kropki z zadaniem - używając składni batch: job .

Zwróć uwagę na interwał zatwierdzania - jest to liczba transakcji, które mają być przechowywane w pamięci przed przekazaniem partii do elementu itemWriter ; będzie przechowywać transakcje w pamięci do tego momentu (lub do napotkania końca danych wejściowych):

5.5. Uruchamianie zadania wsadowego

To wszystko - skonfigurujmy teraz i uruchommy wszystko:

public class App { public static void main(String[] args) { // Spring Java config AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(SpringConfig.class); context.register(SpringBatchConfig.class); context.refresh(); JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); Job job = (Job) context.getBean("firstBatchJob"); System.out.println("Starting the batch job"); try { JobExecution execution = jobLauncher.run(job, new JobParameters()); System.out.println("Job Status : " + execution.getStatus()); System.out.println("Job completed"); } catch (Exception e) { e.printStackTrace(); System.out.println("Job failed"); } } }

6. Wniosek

Ten samouczek przedstawia podstawowe informacje o tym, jak pracować ze Spring Batch i jak z niego korzystać w prostym przypadku.

Pokazuje, jak możesz łatwo opracować potok przetwarzania wsadowego i jak możesz dostosować różne etapy odczytu, przetwarzania i pisania.

Pełna realizacja tego podręcznika można znaleźć w projekcie github - jest to projekt oparty Eclipse, więc powinno być łatwe do importu i biegać jak to jest.