Manueller Dateiimport
Ein eigener Importer liest Zeilen, überspringt den Header und mappt sie in einem Durchlauf.
Vollständiger Legacy-Modern-Vergleich aller Java-Klassen, Tests, Maven-POMs und relevanten Ressourcen dieses Moduls. Jede Datei ist genau einmal einer fachlich passenden Vergleichsgruppe zugeordnet.
Ein eigener Importer liest Zeilen, überspringt den Header und mappt sie in einem Durchlauf.
Reader, Processor, Writer, Step und Job sind getrennte, wiederanlaufbare Batch-Bausteine.
Die Ähnlichkeit ist eine technische Orientierung auf normalisiertem Quelltext. Sie ersetzt keine fachliche Bewertung.
| Vergleichsgruppe | Legacy-Dateien | Modern-Dateien | Legacy-Zeilen | Modern-Zeilen | Ähnlichkeit |
|---|---|---|---|---|---|
| Build-Konfiguration | 1 | 1 | 31 | 52 | 42% |
| Bootstrap und Konfiguration | 1 | 2 | 13 | 14 | 20% |
| Datenmodell und Beispieldaten | 2 | 2 | 10 | 10 | 90% |
| Parser/Importer und Batch-Job-Test | 3 | 2 | 44 | 91 | 15% |
Das Pair-POM bindet die Legacy- und Modern-Submodule zusammen und gehört deshalb ebenfalls zur vollständigen Codeabdeckung.
Manuelle Batch-Abhängigkeiten und Spring Batch werden verglichen.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| pom.xml | pom.xml | Direktvergleich Semantische Ähnlichkeit: 42% |
<?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>at.aydin.academy</groupId>
<artifactId>06batch-pair</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>batch-legacy</artifactId>
<packaging>jar</packaging>
<name>Batch Legacy</name>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project><?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>at.aydin.academy</groupId>
<artifactId>06batch-pair</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>batch-modern</artifactId>
<packaging>jar</packaging>
<name>Batch Modern (Spring Batch)</name>
<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>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>Standalone-Aufruf wird auf Spring-Boot-Batchbetrieb umgestellt.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| BatchLegacyApplication.java | BatchModernApplication.java application.properties | Direktvergleich Semantische Ähnlichkeit: 20% |
package at.aydin.lab.batch.legacy;
import java.nio.file.Path;
public final class BatchLegacyApplication {
private BatchLegacyApplication() {
}
public static void main(String[] args) throws Exception {
Path input = args.length == 0 ? Path.of("src/main/resources/sample-orders.csv") : Path.of(args[0]);
new LegacyBatchImporter().importFile(input).forEach(System.out::println);
}
}package at.aydin.lab.batch.modern;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchModernApplication {
public static void main(String[] args) {
SpringApplication.run(BatchModernApplication.class, args);
}
}spring.datasource.url=jdbc:h2:mem:batchmodern;DB_CLOSE_DELAY=-1
spring.sql.init.mode=always
spring.batch.jdbc.initialize-schema=alwaysDatensatz und Eingabedatei bleiben fachlich direkt vergleichbar.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| OrderRow.java sample-orders.csv | OrderRow.java sample-orders.csv | Direktvergleich Semantische Ähnlichkeit: 90% |
package at.aydin.lab.batch.legacy;
import java.math.BigDecimal;
public record OrderRow(long id, String customer, BigDecimal amount) {
}id,customer,amount
1001,Alpha GmbH,120.50
1002,Beta AG,89.90
1003,Gamma KG,240.00package at.aydin.lab.batch.modern;
import java.math.BigDecimal;
public record OrderRow(long id, String customer, BigDecimal amount) {
}id,customer,amount
2001,Delta GmbH,320.50
2002,Epsilon AG,99.90
2003,Zeta KG,450.00Selbstgebauter Import wird durch deklarative Job-/Step-Konfiguration ersetzt.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| CsvOrderParser.java LegacyBatchImporter.java CsvOrderParserTest.java | BatchConfiguration.java BatchConfigurationTest.java | Direktvergleich Semantische Ähnlichkeit: 15% |
package at.aydin.lab.batch.legacy;
import java.math.BigDecimal;
// Design Pattern: Mapper
// Zweck: Transportformat CSV wird in ein fachliches Objekt übersetzt.
public final class CsvOrderParser {
public OrderRow parse(String line) {
String[] columns = line.split(",", -1);
if (columns.length != 3) throw new IllegalArgumentException("Ungültige CSV-Zeile: " + line);
return new OrderRow(Long.parseLong(columns[0].trim()), columns[1].trim(), new BigDecimal(columns[2].trim()));
}
}package at.aydin.lab.batch.legacy;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
// Design Pattern: Template Method (manuell angedeutet)
// Zweck: Lesen, Überspringen, Parsen und Schreiben bilden einen festen Batch-Ablauf.
public final class LegacyBatchImporter {
private final CsvOrderParser parser = new CsvOrderParser();
public List<OrderRow> importFile(Path path) throws IOException {
try (var lines = Files.lines(path)) {
return lines.skip(1).filter(line -> !line.isBlank()).map(parser::parse).toList();
}
}
}package at.aydin.lab.batch.legacy;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CsvOrderParserTest {
@Test
void parsesRow() {
OrderRow row = new CsvOrderParser().parse("7,Test AG,12.30");
assertEquals(7, row.id());
assertEquals(new BigDecimal("12.30"), row.amount());
}
}package at.aydin.lab.batch.modern;
import javax.sql.DataSource;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemProcessor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
// Design Pattern: Pipeline
// Zweck: Reader, Processor und Writer sind austauschbare, getrennt testbare Batch-Stufen.
public class BatchConfiguration {
@Bean
FlatFileItemReader<OrderRow> orderReader() {
return new FlatFileItemReaderBuilder<OrderRow>()
.name("orderReader")
.resource(new ClassPathResource("sample-orders.csv"))
.linesToSkip(1)
.delimited()
.names("id", "customer", "amount")
.fieldSetMapper(fieldSet -> new OrderRow(
fieldSet.readLong("id"),
fieldSet.readString("customer"),
fieldSet.readBigDecimal("amount")))
.build();
}
@Bean
ItemProcessor<OrderRow, OrderRow> positiveAmountProcessor() {
return item -> item.amount().signum() > 0 ? item : null;
}
@Bean
JdbcBatchItemWriter<OrderRow> orderWriter(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<OrderRow>()
.dataSource(dataSource)
.sql("insert into imported_order(id,customer,amount) "
+ "values (:id,:customer,:amount)")
.itemSqlParameterSourceProvider(item -> new MapSqlParameterSource()
.addValue("id", item.id())
.addValue("customer", item.customer())
.addValue("amount", item.amount()))
.build();
}
@Bean
Step importOrders(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
FlatFileItemReader<OrderRow> orderReader,
ItemProcessor<OrderRow, OrderRow> positiveAmountProcessor,
JdbcBatchItemWriter<OrderRow> orderWriter) {
return new StepBuilder("importOrders", jobRepository)
.<OrderRow, OrderRow>chunk(10, transactionManager)
.reader(orderReader)
.processor(positiveAmountProcessor)
.writer(orderWriter)
.build();
}
@Bean
Job importOrderJob(JobRepository jobRepository, Step importOrders) {
return new JobBuilder("importOrderJob", jobRepository)
.start(importOrders)
.build();
}
}package at.aydin.lab.batch.modern;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BatchConfigurationTest {
@Test
void recordKeepsValues() {
OrderRow row = new OrderRow(1, "Test", new BigDecimal("10.00"));
assertEquals("Test", row.customer());
}
}