こんにちは、さるまりんです。
Excelでデータを作ってCSVにする。この時にゼロ始まりの文字列からゼロが落ちてしまうことってありませんか?
これでおかしなことが起こってしまったのでその回避方法を記しておきます。
CSVの時刻を表すフィールドに71657
の文字列がありました。時刻なので071657
→07:16:57
の意味です。
が、プログラムの中でTime
型(java.sql.Time
)に変換する時になぜか00:05:07
になってました。
なぜ?
この値、71657
ミリ秒として時刻に変換すると00:05:07
になります。
プログラムで見るとこんな感じです。
long ms = 71657;
Time t = new Time(ms);
System.out.println(t);
↓が出力されます。
00:05:07
あら、思ってもなかった誤変換です。
こんなことが起きないようにFieldSetMapper
をカスタマイズしてゼロ埋めしてから処理するようにします。
流れとしては
FlatFileItemReader
で文字列のまま取得FieldSetMapper
で桁数を直す (6桁にゼロ埋め)ItemProcessor
でTimeに変換
です。
データを読み込むエンティティクラスMyEntity
はこんな感じです。
import java.sql.Time;
public class MyEntity {
private String rawTime; // 読み込み時の文字列(ゼロ埋め済み)
private Time time; // Time 型に変換後
// Getter & Setter
public String getRawTime() {
return rawTime;
}
public void setRawTime(String rawTime) {
this.rawTime = rawTime;
}
public Time getTime() {
return time;
}
public void setTime(Time time) {
this.time = time;
}
}
やってみます。
1. FieldSetmapper
で6桁にゼロ埋めする
まず、FlatFileItemReader
のFieldSetMapper
をカスタマイズして、CSVを読み込む時に71657
を071657
に変換します。
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public class CustomFieldSetMapper implements FieldSetMapper<MyEntity> {
@Override
public MyEntity mapFieldSet(FieldSet fieldSet) throws BindException {
MyEntity item = new MyEntity();
// CSV から読み込んだ文字列
String rawTime = fieldSet.readString("timeColumn");
// 6桁にゼロ埋め
String paddedTime = String.format("%06d", Integer.parseInt(rawTime));
item.setRawTime(paddedTime); // ここでは String のまま
return item;
}
}
2. FlatFileItemReader
にCustomFieldSetMapper
を適用する
@Bean
public FlatFileItemReader<MyEntity> reader() {
FlatFileItemReader<MyEntity> reader = new FlatFileItemReader<>();
reader.setResource(new ClassPathResource("data.csv"));
reader.setLinesToSkip(1); // ヘッダーをスキップ
reader.setLineMapper(new DefaultLineMapper<MyEntity>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames("id", "timeColumn"); // CSV のカラム名
}});
setFieldSetMapper(new CustomFieldSetMapper()); // カスタムマッパー適用
}});
return reader;
}
CustomFieldSetMapper
を使ってゼロ埋めした文字列をMyEntity
のrawTime
に071657
に渡しています。
3. ItemProcessor
でTime
に変換する
ItemProcessor
を実装し、rawTime
(071657
)をTime
型(java.sql.Time
)に変換します。
import org.springframework.batch.item.ItemProcessor;
import java.sql.Time;
public class TimeFormatProcessor implements ItemProcessor<MyEntity, MyEntity> {
@Override
public MyEntity process(MyEntity item) throws Exception {
// "071657" を Time オブジェクトに変換
Time formattedTime = convertToTime(item.getRawTime());
item.setTime(formattedTime); // Time 型のフィールドにセット
return item;
}
private Time convertToTime(String paddedTime) {
int hours = Integer.parseInt(paddedTime.substring(0, 2));
int minutes = Integer.parseInt(paddedTime.substring(2, 4));
int seconds = Integer.parseInt(paddedTime.substring(4, 6));
return Time.valueOf(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
}
4. JobConfiguration
でItemProcessor
を適用する
@Bean
public ItemProcessor<MyEntity, MyEntity> processor() {
return new TimeFormatProcessor();
}
これで71657は07:16:57に、915は00:09:15に、3150は00:30:15に正しく変換してくれます。
読み込みに失敗してエラーで止まってくれたら気づきやすいのですが、知らぬ間に変換されて進んでしまうと気づけない。
テストってとても大切なんですね。
発生しそうなことを色々と想定して安全なプログラムを作っていきたいと思います。
読んでくださってありがとうございました。
それではまた!