0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java】Sprint Boot Batchを使ってCSVファイルのデータをPostgreSQLに登録する方法

Last updated at Posted at 2025-08-03

今回は、Spring Boot Batchを使ってcsvデータをPostgreSQLに登録する方法を記載します。
また、一度登録されたデータは再度取り込めないようにします。

ディレクトリ

「Springスタータープロジェクト」を選択して、Batchプロジェクト名を入力します。
image.png

Spring Boot Batchプロジェクトの作成

SpringBatchプロジェクトの作り方1.png

「Batch」を選択して完了です。
SpringBatchプロジェクトの作り方2.png

ファイルの新規作成

Apllication.javaの修正

SpringBatchApplication.java
package com.packt.cardatabase;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.packt.cardatabase.config"})
@ComponentScan(basePackages = {"com.packt.cardatabase.processor"})
@ComponentScan(basePackages = {"com.packt.cardatabase.listener"})
@ComponentScan(basePackages = {"com.packt.cardatabase.repository"})
@ComponentScan(basePackages = {"com.packt.cardatabase.model"})
public class SpringBatchApplication {

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

}

repositoryクラスの作成

repository/AdminUserRepository.java
package com.packt.cardatabase.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class AdminUserRepository{
	@Autowired
	private JdbcTemplate template;
	
	private static final String EXISTS_CHECK_SQL = "" + 
			"SELECT exists (SELECT * FROM admin_user where id =?)";
	
	public boolean exists(Integer id) {
		return template.queryForObject(EXISTS_CHECK_SQL, Boolean.class,id);
	}
}

Procesorクラスの作成

processor/ValidProcessor.java
package com.packt.cardatabase.processor;

import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.packt.cardatabase.model.AdminUser;
import com.packt.cardatabase.repository.AdminUserRepository;

import lombok.extern.slf4j.Slf4j;

/**
 * 年齢をチェックするItemProcessorです。
 * バリデーションに失敗した場合はnullを返し、そのアイテムはスキップされます。
 */
@Component("ValidProcessor")
@StepScope
@Slf4j
public class ValidProcessor implements ItemProcessor<AdminUser, AdminUser> {

    @Autowired
    private AdminUserRepository repository;
    
    /**
     * ユーザーIDの存在チェックと年齢バリデーションを行います。
     * @param adminUser 処理対象のAdminUserオブジェクト
     * @return 処理後のAdminUserオブジェクト
     */
    @Override
    public AdminUser process(AdminUser adminUser) throws Exception {
        // JdbcTemplateを使用するAdminUserRepositoryのexistsメソッドを呼び出すように修正
        if (adminUser.getId() != null && repository.exists(adminUser.getId())) {
            log.warn("既に存在しているIDのため登録できません::id={}", adminUser.getId());
            return null;
        }
        
        try {
            adminUser.judgeAge();
        } catch(RuntimeException e) {
            log.warn(e.getMessage(), e);
            return null;
        }
        
        return adminUser;
    }
}

Modelクラスの作成

model/AdminUser.java
package com.packt.cardatabase.model;

import lombok.Data;

@Data
public class AdminUser {
	private Integer id;
	private String name;
	private Integer age;
	
	public void judgeAge() {
		if(20 > age) {
			String errorMessage = String.format(
					"20才未満は登録できません::id=%d,name=%s,age=%d"
					, getId(),getName(),getAge());
			throw new RuntimeException(errorMessage);
		}
	}
}

listenerクラスの作成

listener/ProcessListener.java
package com.packt.cardatabase.listener;

import org.springframework.batch.core.ItemProcessListener;
import org.springframework.stereotype.Component;

import com.packt.cardatabase.model.AdminUser;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class ProcessListener implements ItemProcessListener<AdminUser,AdminUser> {
	@Override
	public void beforeProcess(AdminUser adminUser) {
		// Do nothing
	}
	
	@Override
	public void afterProcess(AdminUser adminUser,AdminUser adminUser2) {
		// Do nothing
	}
	
	@Override
	public void onProcessError(AdminUser adminUser,Exception e) {
		log.error("ProcessError::user{},errorMessage={}",adminUser,e.getMessage(),e);
	}
}

listener/ReadListener.java
package com.packt.cardatabase.listener;

import org.springframework.batch.core.ItemReadListener;
import org.springframework.stereotype.Component;

import com.packt.cardatabase.model.AdminUser;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class ReadListener implements ItemReadListener<AdminUser> {
	@Override
	public void beforeRead() {
		
	}
	
	@Override
	public void afterRead(AdminUser adminUser) {
		log.debug("AfterRead:{}",adminUser);
	}
	
	@Override
	public void onReadError(Exception e) {
		log.error("ReadError::errorMessage={}",e.getMessage(),e);
	}
}

listener/WriteListener.java
package com.packt.cardatabase.listener;

import org.springframework.batch.core.ItemWriteListener;
import org.springframework.batch.item.Chunk;
import org.springframework.stereotype.Component;

import com.packt.cardatabase.model.AdminUser;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class WriteListener implements ItemWriteListener<AdminUser> {
	/**
     * 書き込み処理の直前に実行されます。
     * Spring Batch 5.0以降ではChunkオブジェクトを受け取ります。
     * @param items 書き込み対象のアイテムを含むChunkオブジェクト
     */
	@Override
	public void beforeWrite(Chunk<? extends AdminUser> items) {
		log.debug("BeforeWrite::count={}", items.size());
	}
	
	/**
     * 書き込み処理の直後に実行されます。
     * Spring Batch 5.0以降ではChunkオブジェクトを受け取ります。
     * @param items 書き込みが完了したアイテムを含むChunkオブジェクト
     */
	@Override
    public void afterWrite(Chunk<? extends AdminUser> items) {
		log.debug("AfterWrite::count={}, items={}", items.size(), items.getItems());
    }
	/**
     * 書き込み中にエラーが発生した場合に実行されます。
     * Spring Batch 5.0以降ではChunkオブジェクトを受け取ります。
     * @param exception 発生した例外
     * @param items エラーが発生したアイテムを含むChunkオブジェクト
     */
	@Override
    public void onWriteError(Exception exception, Chunk<? extends AdminUser> items) {
		// items.getItems()でリストを取得できます
        log.error("WriteError::errorMessage={}", exception.getMessage(), exception);
        // エラーが発生したアイテムをログに出力する例
        items.getItems().forEach(item -> log.error("Error item: {}", item));
    }
}

configクラスの作成

BatchConfig.java
package com.packt.cardatabase.config;

import java.nio.charset.StandardCharsets;

import org.springframework.batch.core.ItemProcessListener;
import org.springframework.batch.core.ItemReadListener;
import org.springframework.batch.core.ItemWriteListener;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
//import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;非推奨
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
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.ItemWriter;
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.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;

import com.packt.cardatabase.model.AdminUser;

@EnableBatchProcessing
public class BatchConfig {
    /* 非推奨
	@Autowired
    protected JobBuilderFactory jobBuilderFactory;
    */
    /* 非推奨
    @Autowired
    protected StepBuilderFactory stepBuilderFactory;
    */
	
	private final JobRepository jobRepository;
	private final PlatformTransactionManager transactionManager;

    @Autowired
    @Qualifier("ValidProcessor")
    protected ItemProcessor<? super AdminUser, ? extends AdminUser> judgeAgeProcessor;

    @Autowired
    protected ItemReadListener<AdminUser> readListener;

    @Autowired
    protected ItemProcessListener<AdminUser,AdminUser> processListener;

    @Autowired
    protected ItemWriteListener<AdminUser> writeListener;

    @Autowired
    protected sampleProperty sampleProperty;
    
    /**
     * コンストラクタインジェクションでJobRepositoryとPlatformTransactionManagerを注入します。
     * @param jobRepository Spring Batchが自動的に提供するJobRepository
     * @param transactionManager Spring Batchが自動的に提供するトランザクションマネージャー
     */
    public BatchConfig(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    	this.jobRepository = jobRepository;
    	this.transactionManager = transactionManager;
    }
    
    /**
     * CSVファイルからデータを読み込むItemReaderのBean定義です。
     * @return AdminUserオブジェクトを読み込むFlatFileItemReader
     */
    @Bean
    @StepScope
    public FlatFileItemReader<AdminUser> csvReader(){
        String [] columnNameArray = new String[] {"id","name","age"};

        return new FlatFileItemReaderBuilder<AdminUser>()
                .name("userCsvReader")
                .resource(new ClassPathResource(sampleProperty.getCsvPath()))
                .linesToSkip(1)
                .encoding(StandardCharsets.UTF_8.name())
                .delimited()
                .names(columnNameArray)
                .fieldSetMapper(new BeanWrapperFieldSetMapper<AdminUser>(){
                    {
                        setTargetType(AdminUser.class);
                    }
                }).build();
    }
    
    /**
     * ログに処理結果を出力するシンプルなItemWriterのBean定義です。
     * @return AdminUserオブジェクトをログに出力するItemWriter
     */
    @Bean
    public ItemWriter<AdminUser> userItemWriter() {
        return items -> items.forEach(System.out::println);
    }
    
    /**
     * StepのBean定義です。
     * CSVの読み込み、年齢チェック、ログへの出力を行うチャンク指向のStepを定義します。
     * @return 構築されたStep
     */
    @Bean
    public Step validationStep(FlatFileItemReader<AdminUser> csvReader, ItemWriter<AdminUser> userItemWriter) {
        return new StepBuilder("validationStep", jobRepository)
                .<AdminUser, AdminUser>chunk(10, transactionManager)
                .reader(csvReader)
                .processor(judgeAgeProcessor)
                .writer(userItemWriter)
                .listener(readListener)
                .listener(processListener)
                .listener(writeListener)
                .build();
    }
    
    /**
     * JobのBean定義です。
     * validationStepを実行するJobを定義します。
     * @return 構築されたJob
     */
    @Bean
    public Job validateAgeJob(Step validationStep) {
        return new JobBuilder("validateAgeJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(validationStep)
                .build();
    }
    
}

config/JdbcImportBatchConfig.java
package com.packt.cardatabase.config;

import javax.sql.DataSource;

import org.springframework.batch.core.ItemProcessListener;
import org.springframework.batch.core.ItemReadListener;
import org.springframework.batch.core.ItemWriteListener;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
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.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.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.transaction.PlatformTransactionManager;

import com.packt.cardatabase.model.AdminUser;

@Configuration
public class JdbcImportBatchConfig {
    
    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final DataSource dataSource;
    
    // 依存関係を自動的に注入します
    @Autowired
    private ItemProcessor<AdminUser, AdminUser> judgeAgeProcessor;
    
    @Autowired
    private ItemReadListener<AdminUser> readListener;
    
    @Autowired
    private ItemProcessListener<AdminUser, AdminUser> processListener;
    
    @Autowired
    private ItemWriteListener<AdminUser> writeListener;

    private static final String INSERT_SQL =
            "INSERT INTO admin_user(id,name,age) VALUES(:id,:name,:age)";

    public JdbcImportBatchConfig(JobRepository jobRepository, PlatformTransactionManager transactionManager, DataSource dataSource) {
        this.jobRepository = jobRepository;
        this.transactionManager = transactionManager;
        this.dataSource = dataSource;
    }
    
    @Bean
    @StepScope
    public JdbcBatchItemWriter<AdminUser> jdbcWriter(){
        BeanPropertyItemSqlParameterSourceProvider<AdminUser> provider =
                new BeanPropertyItemSqlParameterSourceProvider<>();
        return new JdbcBatchItemWriterBuilder<AdminUser>()
                .itemSqlParameterSourceProvider(provider)
                .sql(INSERT_SQL)
                .dataSource(this.dataSource)
                .build();
    }
    
    /**
     * StepのBean定義です。
     * csvReaderを引数として受け取るように修正しました。
     * @return 構築されたStep
     */
    @Bean
    public Step csvImportJdbcStep(FlatFileItemReader<AdminUser> csvReader){
        return new StepBuilder("CsvImportJdbcStep", jobRepository)
                .<AdminUser, AdminUser>chunk(5, transactionManager)
                .reader(csvReader)
                .listener(readListener)
                .processor(judgeAgeProcessor)
                .listener(processListener)
                .writer(jdbcWriter())
                .listener(writeListener)
                .build();
    }

    /**
     * JobのBean定義です。
     * csvImportJdbcJobの引数にcsvImportJdbcStepを追加しました。
     * @return 構築されたJob
     */
    @Bean("CsvImportJdbcJob")
    public Job csvImportJdbcJob(Step csvImportJdbcStep){
        return new JobBuilder("CsvImportJdbcJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(csvImportJdbcStep)
                .build();
    }
    
    @Bean
    @StepScope
    public FlatFileItemReader<AdminUser> csvReader() {
        // CSVファイルのリーダー設定
        FlatFileItemReader<AdminUser> reader = new FlatFileItemReader<>();
        
        // 1行目(ヘッダー行)をスキップ
        reader.setLinesToSkip(1);  // これで1行目(ヘッダー行)をスキップします
        
        // CSVファイルの場所を指定する
        reader.setResource(new ClassPathResource("sql/adminUser.csv"));
        
        // マッピングするためのLineMapperを設定
        DefaultLineMapper<AdminUser> lineMapper = new DefaultLineMapper<>();
        
        // CSVファイルの各行をAdminUserオブジェクトにマッピングするためのFieldSetMapperを設定
        BeanWrapperFieldSetMapper<AdminUser> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(AdminUser.class);
        
        // 行の分割を行うLineTokenizerを設定(CSVファイルの場合、カンマ区切り)
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setNames("id", "name", "age"); // CSVのカラム名に合わせて変更
        
        // 各設定をLineMapperに設定
        lineMapper.setLineTokenizer(lineTokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);
        
        reader.setLineMapper(lineMapper);
        
        return reader;
    }

}

config/sampleProperty.java
package com.packt.cardatabase.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.ToString;

@Component
@PropertySource("classpath:application.properties")
@Getter
@ToString
public class sampleProperty {
	@Value("${csv.path}")
	private String csvPath;
}

pom.xmlの修正

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.5.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.packt.cardatabase</groupId>
	<artifactId>SpringBatch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBatch</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-batch</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jdbc -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
		<dependency>
		    <groupId>org.mybatis.spring.boot</groupId>
		    <artifactId>mybatis-spring-boot-starter</artifactId>
		    <version>3.0.5</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-devtools</artifactId>
		    <!--<version>4.0.0-M1</version> 削除した。-->
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
		<dependency>
		    <groupId>org.postgresql</groupId>
		    <artifactId>postgresql</artifactId>
		    <version>42.7.7</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <version>1.18.38</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>
				<!--追加-->
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

application.propertyの修正

application.property
spring.application.name=SpringBatch
csv.path=sql/adminUser.csv

application.ymlの作成

application.yml
spring:
  batch:
    job:
      names: CsvImportJdbcJob
    jdbc:
      initialize-schema: always
      schema: classpath:org/springframework/batch/core/schema-postgresql.sql
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/SpringMyBatis
    username: postgres
    password: postgres

logging:
  level:
    '[com.example.SpringBatchSample]': debug

インポートするCSVファイル

sql/adminUser.csv
id,name,age
1,ユーザー1,30
2,ユーザー2,30
3,ユーザー3,30
4,ユーザー4,30
5,ユーザー5,30
6,ユーザー6,30
7,ユーザー7,30
8,ユーザー8,30

サイト

トラブルシューティング

1.アプリケーションエラー
Description:
Parameter 0 of method csvImportJdbcStep in com.packt.cardatabase.config.JdbcImportBatchConfig required a bean of type 'org.springframework.batch.item.file.FlatFileItemReader' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.batch.item.file.FlatFileItemReader' in your configuration.

エラーメッセージの通り、FlatFileItemReader<AdminUser>のBeanが定義されていないため、Spring Boot BatchはcsvImportJdbcStepcsvReaderを注入できません。

FlatFileItemReaderはCSVファイルなどのファイルを読み込むためのリーダーで、これを適切に定義する必要があります。以下のようにJdbcImportBatchConfigクラスにFlatFileItemReader<AdminUser>のBean定義を追加することで解決できます。

1. FlatFileItemReaderのBeanを追加

まず、FlatFileItemReader<AdminUser>を定義します。このリーダーはCSVファイルを読み込むための設定を行います。AdminUserクラスのフィールドにマッピングする必要があります。

JdbcImportBatchConfig.java
@Bean
@StepScope
public FlatFileItemReader<AdminUser> csvReader() {
    // CSVファイルのリーダー設定
    FlatFileItemReader<AdminUser> reader = new FlatFileItemReader<>();
    
    // CSVファイルの場所を指定する
    reader.setResource(new FileSystemResource("path/to/your/csvfile.csv"));
    
    // マッピングするためのLineMapperを設定
    DefaultLineMapper<AdminUser> lineMapper = new DefaultLineMapper<>();
    
    // CSVファイルの各行をAdminUserオブジェクトにマッピングするためのFieldSetMapperを設定
    BeanWrapperFieldSetMapper<AdminUser> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
    fieldSetMapper.setTargetType(AdminUser.class);
    
    // 行の分割を行うLineTokenizerを設定(CSVファイルの場合、カンマ区切り)
    DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
    lineTokenizer.setNames("id", "name", "age"); // CSVのカラム名に合わせて変更
    
    // 各設定をLineMapperに設定
    lineMapper.setLineTokenizer(lineTokenizer);
    lineMapper.setFieldSetMapper(fieldSetMapper);
    
    reader.setLineMapper(lineMapper);
    
    return reader;
}

この設定により、FlatFileItemReaderがcsvImportJdbcStepに注入できるようになります。

2. csvImportJdbcStepのBean定義に変更なし

上記でcsvReader()を定義したことで、csvImportJdbcStepの中でFlatFileItemReaderが問題なく注入されるようになります。以下のコードに変更は必要ありません。

JdbcImportBatchConfig.java
@Bean
public Step csvImportJdbcStep(FlatFileItemReader<AdminUser> csvReader) {
    return new StepBuilder("CsvImportJdbcStep", jobRepository)
            .<AdminUser, AdminUser>chunk(5, transactionManager)
            .reader(csvReader)
            .listener(readListener)
            .processor(judgeAgeProcessor)
            .listener(processListener)
            .writer(jdbcWriter())
            .listener(writeListener)
            .build();
}

2.エラーメッセージ ItemStreamException: Failed to initialize the readerが発生

エラーメッセージ ItemStreamException: Failed to initialize the reader は、FlatFileItemReader の初期化中に問題が発生したことを示しています。これにはいくつかの原因が考えられますが、以下の点を確認することで解決できる可能性が高いです。

1. CSVファイルのパスが正しいか確認

エラーメッセージの原因として、FileSystemResource("path/to/your/csvfile.csv") のパスが正しくないことがよくあります。Spring Batch が実際にファイルを開けない場合、エラーが発生します。

確認ポイント:

path/to/your/csvfile.csv のパスが実際のCSVファイルへのフルパスであることを確認してください。相対パスや絶対パスを使って、正しいパスに変更する必要があります。

ファイルが存在し、アプリケーションがそのファイルにアクセスできる権限があることを確認してください。

例えば、resources フォルダ内にCSVファイルがある場合、次のように変更します:

sample.java
reader.setResource(new ClassPathResource("csvfile.csv"));
3.Parsing error at line: 1 in resource=[class path resource [sql/adminUser.csv]]が発生

FlatFileParseException が発生し、「Parsing error at line: 1 in resource=[class path resource [sql/adminUser.csv]]」とあります。このエラーは、CSVファイルの内容が FlatFileItemReader の期待する形式に一致していない場合に発生します。具体的には、CSVのパース中に予期しない問題が発生したことを意味しています。

エラーの原因

エラーの内容から考えられる原因は、以下の点です:

CSVの1行目(ヘッダ行)がパースされていること:
DelimitedLineTokenizer が id,name,age をデータとして扱おうとするのですが、フィールドのマッピングでエラーが発生しています。

フィールド名の不一致:

BeanWrapperFieldSetMapper がCSVのフィールドを AdminUser クラスのフィールドにマッピングしようとしていますが、何らかの不一致が発生している可能性があります。

今回の場合、CSVヘッダーが原因でしたので下記のように修正しました。

JdbcImportBatchConfig.java
@Bean
@StepScope
public FlatFileItemReader<AdminUser> csvReader() {
    FlatFileItemReader<AdminUser> reader = new FlatFileItemReader<>();
    
    // CSVファイルのリソース設定(class pathリソースの場合)
    reader.setResource(new ClassPathResource("sql/adminUser.csv"));
    
    // 1行目(ヘッダー行)をスキップ
    reader.setLinesToSkip(1);  // これで1行目(ヘッダー行)をスキップします
    
    // マッピングのためのLineMapper設定
    DefaultLineMapper<AdminUser> lineMapper = new DefaultLineMapper<>();
    
    // 行をフィールドにマッピングするためのFieldSetMapper
    BeanWrapperFieldSetMapper<AdminUser> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
    fieldSetMapper.setTargetType(AdminUser.class);
    
    // CSVの区切り文字(カンマ)でトークン分割
    DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
    lineTokenizer.setNames("id", "name", "age");  // CSVのカラム名に合わせて設定
    
    // LineMapperに設定を追加
    lineMapper.setLineTokenizer(lineTokenizer);
    lineMapper.setFieldSetMapper(fieldSetMapper);
    
    reader.setLineMapper(lineMapper);
    
    return reader;
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?