そういえばそうだった。ずいぶん昔悩んだことがあるのだがすっかり忘れていた。

ファイルをアップロードするのではなく、単純にフルパスだけ Controller に渡してやって、実際のファイルの読み込みは Javaで行いたかったのだが、そういえばセキュリティ上の対策でローカルファイルのフルパスはブラウザからサーバ側には渡せないんだった。

一応、ファイル名を取ってくる処理を Spring Boot + Thymeleaf で書いてみたので、備忘録で載せておく・・・が、ファイル名しか取れないんじゃ意味がないんだよな(^^;;;
(ちなみに、これだけなら Thymeleaf で書かなくても、普通の HTML でいいじゃん・・・というご意見はあるでしょうが、フルパス取れたら色々拡張していこうと思ってたのよね)

■resources/templates/SelectLogs.html

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org" lang="ja">

<head>
	<title>対象ログファイル選択

	<script>
		document.addEventListener('DOMContentLoaded', function() {
			document.getElementById('fileInput').addEventListener('change', function(e) {
				// 選択されたファイルを取得
				const file = e.target.files[0];
				const displayArea = document.getElementById('fileNameDisplay');

				if (file) {
					// ファイル名を表示要素にセット
					displayArea.textContent = file.name;
				}
				else {
					// キャンセルされた場合などのリセット処理
					displayArea.textContent = 'ファイルが選択されていません';
				}
			});
		});
	</script>
</head>
<body>
	<form>
		<!-- ファイル選択ダイアログ -->
		<input type="file" id="fileInput" style="display: none;" />

		<!-- ダイアログを呼び出すボタン -->
		<button type="button" onclick="document.getElementById('fileInput').click();">
		ファイルを選択
		</button>

		<!-- ファイル名を表示する要素 -->
		<span id="fileNameDisplay">ファイルが選択されていません
	</form>
</body>

</html>

※注意
ちなみに、addEventListener な処理を頭(head の中とか)に書くと、まだ実際には body部の要素(例えば、'fileNameDisplay' って名前の span要素)が読み込まれていないので、document.getElementById('fileNameDisplay')って指定でエラーになって addEventListener されない。
この場合は、この 'change'イベントの addEventListener を 'DOMContentLoaded'イベント のaddEventListener で囲ってやれば、すべての読み込みが終了後に 'change'tイベント追加処理が実行されるので問題ない。
body の一番後ろに script を書けば良いんだけど、古い人間なので、script は head 内に書きたいのよね(笑)


■java/com/netandfield/test/controller/SelectLogsController.java

package com.netandfield.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/SelectLogs")
public class SelectLogsController {

	/**
	* 初期表示
	* @return
	* @throws Exception
	*/
	@GetMapping
	public String init() throws Exception {

		return "SelectLogs";

	}

}

ローカルファイルのパスをどうしても Java に渡す処理を Web技術(HTML,CSS,JavaScript)を使って書きたいのなら、Electronなどで「デスクトップアプリ」として作れってことみたい。

Spring Boot と Electron を組み合わせるというのはありだな。Electron でフロントエンドを作り、Spring Boot で Electronデータの画面とデータのやり取りをするだけの REST API を動かすって形で。
なるほど。しかし、これだと Thymeleaf の出番がない?(^^;;; そもそも Thymeleaf の勉強もしたくて、Spring Boot + Thymeleaf の業務システムを作ろう思ったのよね。

ま、ブラウザがサーバにフルパスを渡せられるようになったら「セキュリティ上、大変問題」ってのはわかるので、Electron の勉強するか・・・
Kato83 さんの Web サイト「Pulog プログラムのブログ 略してプログ」の中の「Spring BootでSpring BatchのChunkモデルを試す」というエントリーを参考に Spring Batch を勉強させてもらっているんですが、この記事が書かれたのが約 6年前ということで対象バージョンが古い。今は SpringBatch も 6 までバージョンが上がっていて、そのままソースを拝借したのでは動きません。
 
そこで、Spring Batch 6 に対応した修正を行ったので、ここで公開しておきます。
俺以上に Spring Batch 素人で、それでも「とりあえず動かしてみんとようわからん」という方向けです(笑)
 
プロジェクトの依存関係(Dependencies)とかは Kato83 さんのサイトを参考にしてください。
ちなみに、今回は com.netandfield.test フォルダの下に全ファイル配置しています。
(なので、Config用のクラスの中で、User や UserRepository の import は行っていません)
 
 
■BatchTest1/src/main/resourses/application.properties
 
spring.application.name=BatchTest1
spring.datasource.url=jdbc:h2:./.data/h2/db;MODE=MySQL
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
 
 
■BatchTest1/src/main/java/com/netandfield/test/User.java
 
package com.netandfield.test;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity(name = "`user`")
@NoArgsConstructor
public class User {
    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer id;
    public String firstName;
    public String lastName;
}
 
※注意
h2 データベースを MODE=MySQL で使っているせいか、テーブル名=user が MySQL の予約語 USER とかぶって expected "identifier" エラーとなってしまいます。
@Entity(name = "user") の user をバックスラッシュで囲んで、@Entity(name = "`user`") のようにしてください。
 
 
■BatchTest1/src/main/java/com/netandfield/test/UserRepository.java
 
package com.netandfield.test;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository {
}
 
 
■BatchTest1/src/main/java/com/netandfield/test/BatchTest1Application.java
 
package com.netandfield.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BatchTest1Application {

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

}
 
 
■BatchTest1/src/main/java/com/netandfield/test/BatchTest1Configuration.java
 
package com.netandfield.test;

import java.text.Normalizer;

import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.parameters.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.infrastructure.item.ItemProcessor;
import org.springframework.batch.infrastructure.item.data.RepositoryItemWriter;
import org.springframework.batch.infrastructure.item.data.builder.RepositoryItemWriterBuilder;
import org.springframework.batch.infrastructure.item.file.FlatFileItemReader;
import org.springframework.batch.infrastructure.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.infrastructure.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.transaction.PlatformTransactionManager;

import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class BatchTest1Configuration {

    @Autowired
    public UserRepository userRepository;

    /**
     * sample.csv ファイルを読み込む
     * @return バッチ処理用 Reader item
     */
    @Bean
    public FlatFileItemReader reader() {
        return new FlatFileItemReaderBuilder()
                .name("userItemReader")
                .resource(new FileSystemResource("sample.csv"))
                .delimited()
                .names("firstName", "lastName")
                .fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
                    setTargetType(User.class);
                }})
                .build();
    }

    /**
     * Reader から渡されたオブジェクトを保存する形に変換する
     * 今回はアルファベットの大文字変換と、半角カナを全角カナに変換している
     * @return 変換後に保存するアイテム
     */
    @Bean
    public ItemProcessor processor() {
        return user -> {
            final var firstName = Normalizer.normalize(user.getFirstName().toUpperCase(), Normalizer.Form.NFKC);
            final var lastName = Normalizer.normalize(user.getLastName().toUpperCase(), Normalizer.Form.NFKC);
            final var transformedUser = new User(firstName, lastName);

            log.info("before converted : " + user);
            log.info("transformed user : " + transformedUser);
            return transformedUser;
        };
    }

    /**
     * Processor から渡されたオブジェクトを書き込む(保存処理をかける)
     * @return 保存処理用のリポジトリ
     */
    @Bean
    public RepositoryItemWriter writer() {
        return new RepositoryItemWriterBuilder()
                .repository(userRepository)
                .methodName("save")
                .build();
    }

    /**
     * 1つのバッチ処理の処理順やエラーハンドリング等の定義を行う
     * @return Jobを返却
     */
    @Bean
    public Job importUserJob(JobRepository jobRepository, Step step1) {
        return new JobBuilder("importUserJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .flow(step1)
                .end()
                .build();
    }

    /**
     * Jobの中に含まれるステップを定義する
     * @return ステップを返却する
     */
    @Bean
    public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("step1", jobRepository)
                .chunk(10)
                .reader(reader())
                .processor(processor())
                .writer(writer())
                .build();
    }

}
 
 
テストデータは、Kato83 さんのものをそのまま使っています。
 
■BatchTest1/sample.csv
 
ジョン, スミス
田中, 太郎
ジョセフ, ジョースター
Erik, Satie
 
 
これらのファイルを用意して、BatchTest1Application.java を Java アプリケーションとして実行します(俺は Eclipse 上で実行してるもんで)
 
これで、
 
2026-06-09T17:18:46.581+09:00 INFO 684 --- [BatchTest1] [ main] o.s.batch.core.step.AbstractStep : Executing step: [step1]
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.648+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョン, lastName=スミス)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=田中, lastName=太郎)
2026-06-09T17:18:46.649+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ジョセフ, lastName=ジョースター)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : before converted : User(id=null, firstName=Erik, lastName=Satie)
2026-06-09T17:18:46.650+09:00 INFO 684 --- [BatchTest1] [ main] c.n.test.BatchTest1Configuration : transformed user : User(id=null, firstName=ERIK, lastName=SATIE)
2026-06-09T17:18:46.818+09:00 INFO 684 --- [BatchTest1] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 236ms
2026-06-09T17:18:46.821+09:00 INFO 684 --- [BatchTest1] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [FlowJob: [name=importUserJob]] completed with the following parameters: [{JobParameter{name='run.id', value=1, type=class java.lang.Long, identifying=true}}] and the following status: [COMPLETED] in 248ms
2026-06-09T17:18:46.831+09:00 INFO 684 --- [BatchTest1] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
 
※一部抜粋
 
このような結果が得られます。やった(笑)
 
今日は兵庫県内をツーリング。
ハイエースに愛機 Ducati Monster S2R 1000 を乗せて、まずは 4時間かけて兵庫県たつの市御津町の「道の駅 みつ」へ。4時間かかった(^^;;;
そこで S2R をおろして、まず向かったのは姫路城である。ベタな観光地だが、一度は本物を見ておきたいからな、城好きとしては。

・・・すんません、本当はそんなに城好きじゃありません。日本人の一般教養として押さえておきたいだけで(笑)

20260606_himejijyo1.jpeg 20260606_mitsu1.jpeg 20260606_himejigokoku1.jpeg

話を戻す。
「道の駅 みつ」からは主に二つのコースがあって、バイパス(?)を抜けると 30分。2号線を走ると 40分みたいな感じ?(詳しくは自力で調べてください(笑)。Google Maps とかで)
とりあえず行きは時間の短いコースで行くことにする。「道の駅 みつ」の駐車場は 17:30には閉鎖してしまうようなので、それまでに確実に帰ってこないといけないからな。

すでに時間は 15時。現地に着くのは 15:30頃である。どうも姫路城は「開城時間:午前9時00分から午後5時00分まで(閉門は午後4時00分)」とのことなので、中を見ることはできそうにないが、まあ、外観だけでも拝めればね・・・と。

拝めました(笑)。
姫路城の真正面の交差点から、遠くて少し霞んでいるけど、白鷺城と呼ぶに相応しい美しい姿を拝めましたよ(笑)

うん、これで満足。今後姫路城の話題が出た時、「ああ、俺も本物見たことあるよ。」と言えるぞ(笑)。「え?実物見たことないの?実物見ないと、なかなかあの美しさは理解できないと思うよ」とか言える(笑)
もう、2ヶ月以上前の話。

朝、7時過ぎの電車で岩国に帰ったんだが、電車に乗る前に腹ごしらえ。
横川駅には立ち食いうどんはないので、横川商店街の「立喰い 辰屋」へ。

20260327_tatsuya1.jpeg 20260327_tatsuya2.jpeg 20260327_tatsuya3.jpeg

もう、俺が以前横川に住んでいた 40年近く前からある店だ。実際、もっと昔からあるんだろう。
十日市に仕事場を借りていたときは通勤に横川駅を使っていたのでたまに寄ることもあったが、部屋を解約してからはすっかりその機会もなくなってしまった。

実に 8年ぶりの訪店である。

あの頃はおじいちゃんとおばあちゃんが店を切り盛りしていたが、今は俺と同世代のおっちゃん一人である。あのおじいちゃんの息子さんなのか、まったく赤の他人なのか・・・

若干戸惑いながら、俺は「天ぷらうどん」を注文。それに「しゃけのおむすび」を一つ付ける。
8年前は 400円だった天ぷらうどんも、今では 550円だ。店主の代替わりもあり、本当に時の流れを感じるなあ。

ああ、このやわやわの蒸し麺とでっかい天ぷら。最高。
そしてスープ。ここのスープの味が好きなのよ。「駅麺家」とは微妙に違う味。このスープとどろどろに溶けた天ぷらをずずずずずと啜る快感(笑)

帰りに、前日余ったものであろう「おつとめ品」のおはぎを嫁さんへのお土産に購入。
ごっつい大将の口から「早めに食べてくださいね」と優しい言葉。やっぱ立ち食いうどんは人情だ(わけわからん〆ですまん(^^;)
今日は家族で大竹の「階杉」に出かけ、嫁さんは白髪ネギラーメンを、娘は汁なし担々麺を「辛さ5」で注文。

俺はいつものようにまずはビールと唐揚げ5個を注文した上で、汁なし担々麺を「ヨガインフェルノ」(1番辛いやつ)で注文した。

半年ぶりのヨガインフェルノである。

20260531_kaisugi1.jpeg 20260531_kaisugi2.jpeg 20260531_kaisugi3.jpeg

辛い・・・辛すぎる・・・失敗であった(^^; ここまで辛いと食事を楽しめない(^^;;; 「俺はこんなに辛いものでも平気で食べられるぜ」って自慢したいアホ以外、辛さは5までで止めておくのが良いと思う(笑)

試しに俺のヨガインフェルノを二口、三口と食べた娘は唇が痛いと悶絶していた(笑)

そして俺も、食後に温かいコーヒーを飲んだら唇が痛いは、18時頃に横川の仕事部屋に戻ったんだけどなんか気がついたら時計の針は22時を回っていた。
魂が抜けてしまっていたようだ・・・辛いものは脳を破壊するしな(笑)

最後に「追い飯」を残った汁に混ぜてフィニッシュするんだけど、ほんま、最後の最後まで辛かった・・・

半年前のブログで「しっかり夜中に腹が痛くなって目が覚めた」と書かれている。今夜、俺は大丈夫なのだろうか?(^^;;;
20260530_spring1.jpg

とりあえず、

Spring Boot DevTools
Lombok
Spring Batch
Spring Data JPA
PostgreSQL Driver

この辺を使用すればバッチ組めるんかな?
Web 関係のものが含まれていると勝手に tomcat が起動したりするんで、ちゃんと非使用にしておこう。

で、DB関係は Spring Data JPA を設定すれば(JPA で DB 操作を書くなら?)、Spring Data JDBC はいらんのよね?

今やってる案件は JDBC を使って、ごりごり生の SQL を書いてるんだけど、勉強のために家では JPA 使ってみようかなっと。
昨夜は高校の同級生N◎と本川町の「鉄華」へ。鉄板焼屋なのに日本酒や焼酎の品揃えが充実している店だ。

20260528_tekka1.jpeg
早速俺はこの日のおすすめであった「賀茂金秀」を注文したのだが、店の女の人が「それよりこっちはどうですか?」と勧めてくれたのが「楽器正宗 別撰 垂れ口」であった。本醸造酒なんだけど、これが上品な甘みのある俺好みのクソ旨酒であった。

これよ、これ。これがなぜかお好み焼屋に酒を飲みに通ってしまう理由なのよ。この酒のチョイスはなんなん。すごいわ。

で、いつものようにお好み焼ではなくセロリの浅漬やら大根のビール漬け、らっきょうなどジジ臭いツマミを突きながら、どうしたら新井カープ監督が辞任してくれるか熱く語り合ったのであった。

ほんま、まだまだ、もう一年やるんか?新井!!
怒りで腹が減ってきたので、そこで海鮮お好み焼を一枚。それをN◎と分け合いながら生ビール。

おお!!今日の20時からゾンビタバコの羽月が会見をするというニュースをゲット(実際は会見ではなく動画配信であった)!!

「頼むからゾンビタバコを吸ってるとき隣に新井監督が居ましたって言うてくれぇ!!」「ついでにオーナーも居ましたって言うてくれぇ!!」

もう、酒を呑んでないとやってられんわ!・・・そんな俺たちが次に呑んだのは新潟の日本酒「〆張鶴 純 純米吟醸酒」。
これ、昭和天皇が愛飲されてた酒なんだって。いや、もちろん「純」を呑んでたわけではなく、多分俺たちは口にできない「〆張鶴 帝 MIKADO」とかそういう特別な酒なんだと思うけど(笑)

20260528_tekka2.jpeg
俺達も〆張鶴を口にすると自然と一人称が「朕」となる。「朕は新井監督の解任を要求するぞよ」「朕はハジメオーナーの引退を要求するぞよ」そんなことを言いながら〆張鶴を呑み、新たに注文したせせりを焼いたのやタコをオリーブオイルで炒めたやつなんかをつつく。うめぇ。
「〆張鶴 純 純米吟醸酒」は辛くてさっぱりした感じ。スーパーなんかで売ってる「上善如水」みたいな味やなあ。ごくごく行ける(笑)。危険な酒や。俺的にはこの前に呑んだ「楽器正宗」のほうが好みだった。

そうしてすっかり気分は天皇になった俺たちであったが、羽月の話はまったく期待外れで、「朕はもうがっかりや」と言いつつ芋焼酎のお湯割りを一杯やって店を出たのであった・・・

いやあ、しかし、「鉄華」はやっぱり良い。
俺がこよなく愛する「どんどん」。このブログにも何度も出てくる焼き肉とラーメンの店だ。所在地は十日市町。

20260512_dondon1.jpeg
40年近く前、この店ができたときからのファンである。なにせ当時の俺は田舎者で、ラーメンといえば国道沿いのドライブインで出てくる醤油ラーメンや、「どさん子ラーメン」の味噌ラーメンくらいしか知らなくて(あ、屋台の塩豚骨の味は知ってたけど(笑))、「どんどん」で初めて食べた「ちゃんとした豚骨ラーメン」の味に一発で虜になったのだ。

ちなみに当時の「どんどん」の焼き飯はクソまずかったけど(笑)。

決して「常連」ではなかったが、それ以来、細く薄くずっと「どんどん」のラーメンとともに生きてきたのである(笑)

20260512_dondon2.jpeg
そんな「どんどん」もすっかり変わった。5/12に土橋で作業をした帰りに久しぶりに「どんどん」に寄ったが、店の中には若い男性3人と以前からいた給仕の中年女性が一人。大将は引退されたのか?すっかり若返っている。店員多すぎっと思ったが、一人は入ったばかりのバイト君のようで色々説明を受けながら作業をしていた。(大事だからもう一度言うが)若返ってるなあ(笑)

しかも、注文がタッチパネル端末になってるやん!!これが一番驚いたわ。時代やな。そして完全に「どんどん」は第2世代に移行したんやな。

まずは「センマイ天」880円也を肴に「生ビール」600円也や!
モツの中ではセンマイが一番好きやね。ザラザラした舌触りと噛み切るときのザクザクという食感。

半分ほど食べたところで、ラーメンを注文。
いつもの「ニンニクラーメン」を注文・・・しようとしたら、なんとトッピングメニューがあるやないの。これはタッチパネル端末による注文ならではやな。
そこで「激辛」というのを追加する。これが 100円で合計1,100円也。

20260512_dondon3.jpeg
いやあ・・・激辛とニンニクは別に食べるべきやったね。「激辛」でニンニクの風味がよくわからん(^^;; これは失敗だった。
「どんどん」のラーメンの味がちゃんと引き継がれているかわからんかった(笑)。

麺が変わってないのはいいね。代替わりしたとき、自分色を出そうとして麺を変えちゃう人もいるみたいだけど、そんな暴挙には及んでいないようで(笑)。(業者製だと思うが)やや細めの中太ストレート麺がええのよ。

そして、次回はちゃんと味のわかるラーメンを注文して「どんどん」の味が引き継がれているかを確認せねば・・・
多分、これだけ長い期間通っているにもかかわらず、余計な話は一切せず、いつもうつむき加減でラーメン食べて速攻帰る俺のことを先代も一切覚えていないであろう、そんな「裏の常連客」として・・・(笑)
いやあ、試しに色々なサイトのソースを参考に Spring Batch を試してたんだけど、一向に実行できない(^^;
ソース上のエラーは全部消しても、実行時に

***************************
APPLICATION FAILED TO START
***************************

みたいなエラーを出して止まってしまう。原因は色々だが、どこが問題なのかわからない・・・
ただ、調べていくうちに、どうも参考にしているサイトの Spring Batch が古いのが問題なのではないか?という気がしてきた。

なので、まさに俺の環境(最新の Spring Batch 6 / Spring Boot 4 環境)で説明がされている IK.AM さんの


というページを参照。

結局、下のようなソース(著作権は IK.AM さんにあると思いますが、一応、若干 import 先が違っていたりするので公開します)でついにバッチ実行ができた。

<BatchTestApplication.java>

package com.netandfield.test;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing
public class BatchTestApplication {

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

}

<config/JobConfig.java>

package com.netandfield.test.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.infrastructure.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class JobConfig {

    private final Logger log = LoggerFactory.getLogger(JobConfig.class);

    private final JobRepository jobRepository;

    public JobConfig(JobRepository jobRepository) {
        this.jobRepository = jobRepository;
    }

    @Bean
    @StepScope
    public Tasklet helloTasklet() {
        return (contribution, chunkContext) -> {
            log.info("Hello World!");
            return RepeatStatus.FINISHED;
        };
    }

    @Bean
    public Step step1(Tasklet helloTasklet) {
        return new StepBuilder("step1", jobRepository).tasklet(helloTasklet)
                .build();
    }

    @Bean
    public Job job1(Step step1) {
        return new JobBuilder("job1", jobRepository).start(step1).build();
    }

}

この2ファイルを作成。(俺は Eclipse 上で作成しているので)プロジェクトエクスプローラーで BatchTestApplication.java を選択し、右ボタンメニューから「デバッグ」→「Javaアプリケーション」で実行される。

やっと Eclipse のコンソールに

[BatchTest] [  restartedMain] com.netandfield.test.config.JobConfig    : Hello World!

と表示された。

しかし、Spring Batch 6 を使って説明している Web ページ、少ないなあ・・・
「まずは丸々コピーで参考サイトのソースを持ってきて、それを実行しながらプログラムの勉強をする」スタイルの俺からすると、「プログラムと実行結果を見比べながら記述内容の確認をして言語を理解していく」ことができないので、「動かないことには動くようにできない」というパラドックスにハマってしまったのであった・・・

エロい、いや、偉い人たち、最新環境での参考ページ制作をよろしくお願いします(笑)
別に差別的な意味ではなく、ただ純粋に県営住宅は怖い(^^;。あの高層住宅ね。個人的に「広島の九龍城砦」と思っている(笑)

20260319_sumi1.jpeg
俺は住宅内にインターネット関係の仕事で入って住民と触れ合ったこともあるが、日本語がほぼ通じない外国人の人も多いし(配偶者が日本人?)、父子揃って無職で昼間っから酒飲みながら、父子揃ってやたら携帯でどっかに電話してる謎行動の日本人家族もいた。ベランダに支持政党の幕や旗を掲げてる人も一人や二人じゃないし。日の丸掲げて歩いてたら絶対絡まれそうな雰囲気ビンビンである。
まあ、中に知人なんかがいる人は「全然怖くないわ」とおっしゃるんでしょうが(もちろんまともな人も実際居る)、そうでない人にとってはやはり怖い場所だと思う。

・・・が、そんな場所だからこそ足を踏み入れてみたいのだ(笑)

この巨大な県営住宅の中には飲み屋や飯屋が入った商店街がある。3月の下旬だがいつもより早い時間に仕事が終わった俺は、いつもならまだ仕事をしている 17時を少し回った時間にその商店街を訪ねたのである。暗くなったら怖いもの(笑)。

20260319_sumi2.jpeg
人はほとんど歩いていない。でも、所々に置かれたベンチにはやたら老人が座っていて、じろじろとこちらに遠慮のない視線を向けてくる、「他所者」ってバレちゃってるんだろうなあ(^^;;;

そして俺が訪店したのは「居酒屋 すみ」。L字のカウンターだけの小さな店である。

入口の引き戸を開けて中に入ると、カウンターの中のおばあさんと、一人酒を呑んでいた常連らしきおじいさんが驚いた顔で俺を見る。明らかに「え?誰?誰?お願いです、殺さないで」と思っているのがビンビン伝わってくる怯えを含んだ目だ。「客だよ、客」と思いながら俺は構わず中に入った。二人の老人の緊張感が高まる。さすがに俺も心配になり、「あの、入っても大丈夫ですか?」と訪ねた。そこでおばあさんはハッと我にかえったかのように「ど、どうぞ」とカウンターを手で指し示すのであった。

いやあ、中は小料理屋と場末のスナックを足して二で割ったような感じ。

20260319_sumi3.jpeg
俺は黒板にかかれていた「今日のおすすめ」からアジの刺し身をチョイスした。なんか、常連は我慢して食べるような生臭いのが出てきそうと勝手に思っていたのだが、これがまたサイズも良いし、臭みなんかも全然無い、脂の乗った美味い刺身が出てきたのである。ほっほう。

こいつを「本洲一」の熱燗とやる。「本洲一」は「千福」なんかと同じ「アル中寸前の酔っ払い親父」たちを対象とした大衆酒で、日頃口にすることはまずないんだけど、こういう店で飲む熱燗は美味かったなあ(笑)

というわけで、酒も肴も予想外(失礼)に美味かった(笑)

そして俺は、注文以外には一言も発することなくこの店をあとにしたのであった。女将さんと常連客のホッとした思いを背中に感じながら(笑)

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら