今回は、Spring Boot Batch
を使ってcsv
データをPostgreSQL
に登録する方法を記載します。
また、一度登録されたデータは再度取り込めないようにします。
ディレクトリ
「Springスタータープロジェクト」を選択して、Batchプロジェクト名を入力します。
Spring Boot Batchプロジェクトの作成
ファイルの新規作成
Apllication.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クラスの作成
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クラスの作成
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クラスの作成
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クラスの作成
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);
}
}
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);
}
}
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クラスの作成
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();
}
}
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;
}
}
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の修正
<?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の修正
spring.application.name=SpringBatch
csv.path=sql/adminUser.csv
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ファイル
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はcsvImportJdbcStep
でcsvReader
を注入できません。
FlatFileItemReader
はCSVファイルなどのファイルを読み込むためのリーダーで、これを適切に定義する必要があります。以下のようにJdbcImportBatchConfig
クラスにFlatFileItemReader<AdminUser>
のBean定義を追加することで解決できます。
1. FlatFileItemReader
のBeanを追加
まず、FlatFileItemReader<AdminUser>
を定義します。このリーダーはCSVファイルを読み込むための設定を行います。AdminUserクラスのフィールドにマッピングする必要があります。
@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が問題なく注入されるようになります。以下のコードに変更は必要ありません。
@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ファイルがある場合、次のように変更します:
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ヘッダーが原因でしたので下記のように修正しました。
@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;
}