20260717_kaisugi1.jpeg
この金曜日はお客さんのところでの作業を早めに上がらせてもらい、うちの会社の期末の作業を色々。
そのため、久しぶりに我が社の江波本社に行く必要があって嫁さんに車で送ってもらった。

無事作業も済んで、帰りに娘も連れて「武蔵坊」で汁なし担々麺でも食べていくかということになって盛り上がったのだが、「武蔵坊 横川店」の夜の部が始まるのは 18:00から。今の時刻、17時ちょっと前・・・一時間以上あるやん・・・(^^;

というわけで、さっさと岩国に帰ることにしていつもの「階杉 ゆめタウン大竹店」へ。

俺は「汁なし担々麺」を辛さ 5で。最後のフィニッシュのご飯も欲しかったので、唐揚げ 3個セット(白飯付き)を追加。

しかし、汁なし担々麺気分が盛り上がっていたのは俺だけだったようで、娘は二郎インスパイアの「杉二郎」、嫁さんはシンプルな中華そばを注文した。
ま、こうして選択肢が広いのも階杉の良いところよね。しかもそれぞれの料理のレベルも高いし。

20260717_kaisugi2.jpeg
俺が唐揚げ 3個セット(白飯付き)を注文するのは、上に書いたように「最後に残った汁にご飯を混ぜて食べる」というのの他に、「唐揚げをアテにビールをやる」という目的がある。

ここの唐揚げはそのままでも美味いが、やはりそこはデブの一手間を(笑)

唐揚げに「マヨネーズ」「胡椒」「おろしにんにく」「もどしにんにく(一度乾燥させたものを水で戻したもの。濃厚な風味)」をぶっかけ、そして上にきゅうりの漬物「カッパ漬」を載せ、一個で 2000kcal くらいありそうな「俺スペシャル」を作りあげる。

これがもうビールのつまみとして最高なのよ。

娘に「いる?」と聞くと「いらない」と(笑)。さすが俺の娘、聡い。には手を出さんか(笑)
Amazon Prime Video で契約していた「dアニメストア」を解約した。
実は docomo で「dアニメストア」は直接契約しているので、二重に契約していたということだ。

20260719_danime1.jpg

元々、娘から頼まれて docomo から「dアニメストア」を契約してたんだが、何年か前に Amazon Prime Video から何日か無料視聴できるやつを申し込んでしまい(何のアニメを見たか全然忘れたけど)、そのまま放置していた。毎年 6,600円、今年から 7,920円が無駄に引き落とされていたというわけだ。

娘の PC には docomo の ID/PW で設定しているので、Amazon Prime Video 側は今解約した(笑)

ちゅうても、今俺は「dアニメストア」で「東島丹三郎は仮面ライダーになりたい」を見ているので(今、エピソード17まで視聴済み)、8/6の期限までは視聴可能なオプションで解約した。

実は、なんかよくわからないけど引き落とされてるお金がいくつかあって、今まで(月に数百円とかなので)ほってたんだけど、Amazon Prime も今は使ってない方のアカウントでも契約してて、もう10年くらい使わないのに引き落とされていた。年間 7,200円だから、7~8万円くらいまったく使ってないのに無駄金にしてしまったというわけだ。これも先日解約した。

そうそう。これまた全然使ってなかったのにお袋の家にアイ・キャンのインターネット接続サービスを契約してて、これも毎月2,750円を使わなくなって 5年くらい払い続けてた。5年で 165,000円無駄銭払ってたってことか・・・。これも解約済。

横川に仕事部屋借りてから色々無駄銭の整理が進んでいる。少しでも部屋代の足しにしたいからな(笑)
今まで作ってきた Spring Batch プログラムをコマンドラインから実行可能な JAR ファイルにしてみる。

Maven プロジェクトとして作っているので、以下の手順で。

プロジェクトを選択し、右ボタンメニューから「実行」→「Maven ビルド...」

20260714_maven_buld1.jpg

構成ダイアログが開くので、「ゴール」のところに「clean package」と入力
「ゴール(Goals)」には実行したい Maven のフェーズやプラグインの機能(コマンド)を指定する。
「clean package」は、「過去の生成物を削除し、新しく JAR や WAR などの配布ファイルを生成する」という指定。
テストが不要なら、「clean package -DskipTests」。これで。JAR へのビルドだけが行われる。

20260714_maven_buld2.jpg

「実行」ボタン押下

これでビルドが実行される。

[INFO] Scanning for projects...
[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.
[INFO]
[INFO]  [1m----------------------< [0;36mcom.example:BatchDB2CSV1 [0;1m >---------------------- [m
[INFO]  [1mBuilding BatchDB2CSV1 0.0.1-SNAPSHOT [m
[INFO]   from pom.xml
[INFO]  [1m--------------------------------[ jar ]--------------------------------- [m
[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/3.5.0/maven-clean-plugin-3.5.0.pom
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/3.5.0/
<略>
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-launcher/6.0.3/junit-platform-launcher-6.0.3.jar (246 kB at 2.2 MB/s)
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.netandfield.test. [1mBatchDb2Csv1ApplicationTests [m 16:26:53.111 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class
[com.netandfield.test.BatchDb2Csv1ApplicationTests]: BatchDb2Csv1ApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
<略>
※「ゴール」に -DskipTests を記述していないので、実際に起動する形でテストが行われる
   .  ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v4.1.0)

<略>
2026-07-14T16:26:58.105+09:00  INFO 19504 --- [BatchDB2CSV1] [   main] c.n.test.Listener.LogJobListener         : ★Job started: exportJob
2026-07-14T16:26:58.112+09:00  INFO 19504 --- [BatchDB2CSV1] [   main] o.s.batch.core.step.AbstractStep         : Executing step: [exportStep]
2026-07-14T16:26:58.816+09:00  INFO 19504 --- [BatchDB2CSV1] [   main] o.s.batch.core.step.AbstractStep         : Step: [exportStep] executed in 704ms
2026-07-14T16:26:58.821+09:00  INFO 19504 --- [BatchDB2CSV1] [   main] c.n.test.Listener.LogJobListener         : ★Job finished: exportJob with status: COMPLETED
2026-07-14T16:26:58.824+09:00  INFO 19504 --- [BatchDB2CSV1] [   main] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=exportJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 717ms
<略>
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/vafer/jdependency/2.10/jdependency-2.10.jar (416 kB at 298 kB/s)
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar (20 kB at 12 kB/s)
[INFO] Replacing main artifact C:\HogeHoge\pleiades\workspace\BatchDB2CSV1\target\BatchDB2CSV1-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] The original artifact has been renamed to C:\HogeHoge\pleiades\workspace\BatchDB2CSV1\target\BatchDB2CSV1-0.0.1-SNAPSHOT.jar.original
[INFO]  [1m------------------------------------------------------------------------ [m
[INFO]  [1;32mBUILD SUCCESS [m
[INFO]  [1m------------------------------------------------------------------------ [m
[INFO] Total time:  34.209 s
[INFO] Finished at: 2026-07-14T16:27:09+09:00
[INFO]  [1m------------------------------------------------------------------------ [m
[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.

これで、

C:\HogeHoge\pleiades\workspace\BatchDB2CSV1\target\BatchDB2CSV1-0.0.1-SNAPSHOT.jar

という JAR ファイルが作られた。

しかし、ビルド時にもすげえ色々ダウンロードしてくるなあ。こりゃ、インターネット環境のない場所じゃビルドできんわ(笑)
開発環境を完全にインターネットから切り離してるところって時々あるけど、時代にあってないよなあ・・・

じゃ、実行してみる。ちょっと場所が深いので、C:\work の下に移動しよう。

C:\work\BatchDB2CSV1-0.0.1-SNAPSHOT.jar

では、実行。

・・・の前に、ディフォルトで使われる java.exe はバージョンが古い。今回のバッチは Java 21 での実行を前提にしている。

C:\Users\hogehoge>java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05_Fujitsu_10-30-12_12:00_patch)
Java HotSpot(TM) Client VM (build 23.5.02_FUJITSU_MODIFIED-B04, mixed mode)

開発で使った Java 21 はここ。

C:\Users\hogehoge>C:\HogeHoge\pleiades\java\21\bin\java -version
openjdk version "21.0.5" 2024-10-15 LTS
OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing)

環境変数(PATH)を直しちゃうと、他のプログラムがうまく動かなくなる可能性もあるので、とりあえずフルパス指定で実行しよう。

C:\Users\hogehoge>C:\HogeHoge\pleiades\java\21\bin\java -jar C:\work\BatchDB2CSV1-0.0.1-SNAPSHOT.jar models=1 modelName=未来電話

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v4.1.0)
<略>
2026-07-14T16:47:55.110+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] o.s.b.b.a.JobLauncherApplicationRunner   : Running default command line with: [models=1, modelName=未来電話]
2026-07-14T16:47:55.318+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=exportJob]] launched with the following parameters: [{JobParameter{name='models', value=1, type=class java.lang.String, identifying=true},JobParameter{name='modelName', value=未来電話, type=class java.lang.String, identifying=true}}]
2026-07-14T16:47:55.345+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] c.n.test.Listener.LogJobListener         : ★Job started: exportJob
2026-07-14T16:47:55.360+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] o.s.batch.core.step.AbstractStep         : Executing step: [exportStep]
2026-07-14T16:47:56.095+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] o.s.batch.core.step.AbstractStep         : Step: [exportStep] executed in 735ms
2026-07-14T16:47:56.102+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] c.n.test.Listener.LogJobListener         : ★Job finished: exportJob with status: COMPLETED
2026-07-14T16:47:56.104+09:00  INFO 18536 --- [BatchDB2CSV1] [   main] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=exportJob]] completed with the following parameters: [{JobParameter{name='models', value=1, type=class java.lang.String, identifying=true},JobParameter{name='modelName', value=未来電話, type=class java.lang.String, identifying=true}}] and the following status: [COMPLETED] in 757ms
2026-07-14T16:47:56.113+09:00  INFO 18536 --- [BatchDB2CSV1] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2026-07-14T16:47:56.135+09:00  INFO 18536 --- [BatchDB2CSV1] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

問題なく実行されたようだ。
Spring Batch 6 のプログラムにデータ検証機能(Validation)を実装してみる。

パラメータ(ジョブ引数)のチェックをするのだが、

  • パラメータとして渡せるのは models、modelName の二つだけ
  • 必須項目はなし
  • models が設定されたときは、数値でなければならない(異常終了)
  • modelName の中身はなんでも良い

という仕様で組み込んでみる。(元ネタは「出力項目を増やすようちょっと修正版 Spring Batch」→「Spring Batch 6 で Listener を追加してログを出す」のソース)


■Validator/OptionalValidator.java

単純な必須チェックなどではない、独自チェックを記述したクラス

package com.netandfield.test.Validator;

import org.springframework.batch.core.job.parameters.InvalidJobParametersException;
import org.springframework.batch.core.job.parameters.JobParameters;
import org.springframework.batch.core.job.parameters.JobParametersValidator;

public class OptionalValidator implements JobParametersValidator {

    @Override
    public void validate(JobParameters parameters) throws InvalidJobParametersException {

        // パラメーター取得
        String models    = parameters.getString("models");;

        // 存在チェック(必須項目ではない)
        if (models == null || models.isEmpty()) {
            return;
        }

        // 存在していれば数値でなければならない
        try {
            Integer.parseInt(models);
        }
        catch(NumberFormatException e) {
            throw new InvalidJobParametersException("Not Number: models=" + models);
        }

    }

}


■Config/BatchConfig.java

import の追加

import java.util.ArrayList;
import java.util.List;

import org.springframework.batch.core.job.parameters.CompositeJobParametersValidator;
import org.springframework.batch.core.job.parameters.DefaultJobParametersValidator;
import org.springframework.batch.core.job.parameters.JobParametersValidator;

import com.netandfield.test.Validator.OptionalValidator; // 作成した validator

バリデータの定義(使用可能なパラメータ(必須項目と任意項目))を追加

    @Bean
    public DefaultJobParametersValidator defaultValidator() {

        // 1. 必須パラメータの配列
        String[] requiredKeys = {};    // 必須項目はなし

        // 2. 任意パラメータの配列
        String[] optionalKeys = {"models", "modelName"};

        // コンストラクタに(必須, 任意)の順番で渡す
        return new DefaultJobParametersValidator(requiredKeys, optionalKeys);

    }

作成した Validator(OptionalValidator クラス)を CompositeJobParametersValidator(コンポジット・ジョブパラメータ・バリデータ)に追加

    @Bean
    public JobParametersValidator compositeValidator() {

        // Validator List 生成
        List<JobParametersValidator> validators = new ArrayList<>();
        validators.add(defaultValidator());
        validators.add(new OptionalValidator());

        // Composit に Validator を入れる
        CompositeJobParametersValidator compositValidator = new CompositeJobParametersValidator();
        compositValidator.setValidators(validators);

        return compositValidator;

    }

Spring Batch では、1つの Job で 1つの Validator しかセットできない。

具体的には、Jobクラス(exportJob)の中で .validator() で指定できるクラスが 1つだけなので、使用可能なパラメータをチェックする標準の Validator(defaultValidator)クラスと、独自チェックの Validator(OptionalValidator)クラスを compositeValidator クラスにまとめ、.validator(compositeValidator()) と指定するわけ。

まとめた Validator を JobBuilder のメソッドチェーンに追加する。

    @Bean
    public Job exportJob(Step exportStep) {
        return new JobBuilder("exportJob", jobRepository)
                .start(exportStep)
                .listener(jobListener) // JobExecutionListenerを追加
                .validator(compositeValidator()) // ValidatorをまとめたcompositeValidatorを追加
                .build();
    }

これで、完了。

実際に実行(「実行の構成」でプログラムの引数を指定)してみる。

パラメータ(引数)が「models=1 modelName=未来電話 mondamin=あり」のとき(許されていないパラメータ "mondamin" あり)

■実行結果ログ(長いので先頭を "~" で省略)

<略>
~ : Running default command line with: [models=1, modelName=未来電話, mondamin=あり]
~ : The JobParameters contains keys that are not explicitly optional or required: [mondamin]
~ : [{JobParameter{name='models', value=1, type=class java.lang.String, identifying=true},JobParameter{name='modelName', value=未来電話, type=class java.lang.String, identifying=true},JobParameter{name='mondamin', value=あり, type=class java.lang.String, identifying=true}}]
~ : The JobParameters contains keys that are not explicitly optional or required: [mondamin]
<略>

と、"mondamin" が「The JobParameters contains keys that are not explicitly optional or required」(JobParameters に、明示的なオプション(任意)または必須として定義されていないキーが含まれています。)と警告されるが、処理は最後まで通る。

「え?InvalidJobParametersException は発生しないの?」って話だが、実は今回の処理には「必須項目」がないので、defaultValidator は自動的に「緩いチェック」にモードを切り替えてしまう。「必須項目」があれば、Spring Batch は「厳格チェックモード」に入り「未定義のパラメータが渡されたら異常終了」するようになる・・・らしい(^^;;;

俺的には「らしい」としか言えないので、エロい人、コメントで詳細教えて(笑)

次に、数値でなければいけないパラメータに「あ」をセットした場合の動きを見てみる。
パラメータ(引数)を「models=あ modelName=未来電話」とする。

■実行結果ログ(長いので先頭を "~" で省略)

これは、以下のようなログを吐いて無事落ちる。

<略>
    at com.netandfield.test.BatchDb2Csv1Application.main(BatchDb2Csv1Application.java:10) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:52) ~[spring-boot-devtools-4.1.0.jar:4.1.0]
Caused by: org.springframework.batch.core.job.parameters.InvalidJobParametersException: Not Number: models=あ
    at com.netandfield.test.Validator.OptionalValidator.validate(OptionalValidator.java:25) ~[classes/:na]
    at org.springframework.batch.core.job.parameters.CompositeJobParametersValidator.validate(CompositeJobParametersValidator.java:45) ~[spring-batch-core-6.0.4.jar:6.0.4]
    at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.createJobExecution(TaskExecutorJobLauncher.java:191) ~[spring-batch-core-6.0.4.jar:6.0.4]

しかし、ぱっと見、「Not Number: models=あ」が見つけられなくて、別の理由で落ちてるのかと思って必死で原因を探したわ(笑)

ああ、年を取って目がしょぼしょぼするのよ(笑)
昨年末の話を書いとく(笑)。このブログは俺の備忘録なので。

20251226_bin1.jpeg
2025年12月26日(金)。仕事納めだったので、飲み仲間のMイさんと十日市の「ニュー美和」で二人だけの納会をして、翌日からキャンプの予定だったのでさっさと一人岩国に帰ったのだが、やっぱりなんか飲み足りない。

嫁さんにお迎えのお願いの電話をするのを保留して、一人中通り商店街を中心に彷徨ったのだがさすがにどこの店も人が多い(^^;;;そりゃ、どこの会社も多分この日が仕事納めだろうからなあ。「満マル」にまで蹴られた俺がやっとたどり着いたのは「中通り食堂 びん」であった。

日ごろであれば決して足を踏み入れることのない雰囲気の店だ。つーか、今まで中通りを何度も行き来したのに、全然その存在に気づかなかった(^^;;;
いわゆる、地元のおじいちゃん、おばあちゃんが通う大衆食堂である。

20251226_bin2.jpeg
なんか入口の看板は「中通り食堂 びん」と書いてあるのだが、入口に掲示されたメニューには「菊川精肉店 和幸内精肉部 食事処 びんBin」と書いてある。「びん」ではなく「びんびん(Bin)」が正しい名前なのか?しかし、そんな男の陰茎力強く勃起した様を表すような店名を付けるものだろうか?(^^;;;

あ、でも、「中通り食堂 びん」の下に赤い小さな字で「Bin」って書かれている。やっぱ「びんびん」なのか?(^^;;; 俺はどきどきしながら入店した。

・・・ワンダーランドだった(笑)

店内には雑貨や民芸品(?)が陳列され、どこにも食堂感がない。奥に進むと入口からは見えづらいところにテーブルが四つほどならんだ小さな食堂ゾーンがあるって感じ。いや、まあ、食堂があってよかった。

俺はとりあえず「酒1杯+小鉢2品」の「晩酌セット」1,000円也を注文。小鉢2品はどちらも肉の姿の全然ない菜っ葉や油揚げなどを煮込んだお年寄り向けなヘルシーなやつ。

20251226_bin3.jpeg
まあ、広島で飲んで腹もそんなに空いてなかったのでこれで十分・・・と思っていたのだが、やはりなんか物足りなくなって、「玉子焼き」と「熱燗(千福)」を追加注文(笑)

玉子焼きにはマヨネーズがついていて、デブの俺も満足(笑)。それに熱々で美味かったしな。

完全に浮いてた一見客の俺にも、店のおじちゃん、おばちゃんはちょこちょこ話しかけてくれて、いわゆる「あったか」系の店だったね。もう少し年を取って枯れきたら通っても良い店かな。

まだまだ毎朝下腹部ビンビンの俺にはまだ早い店だったなあ(笑)
「日々来」のお好み焼で腹は満たされたのだが、まだ時間も早いしもう一杯・・・と、すぐ近くにある「立ち飲み のんどる」を訪店したのである。

ほんとに、「日々来」から徒歩 3分ほど?横川駅裏、いや横川駅北口の横川から三篠方面にのびる道すがらにもたくさんの良い感じの店があって、やっぱ今広島で一番熱いのは十日市~横川~三篠のこのラインではないかと感じるねえ。

20260714_nondoru1.jpeg
「立ち飲み のんどる」は以前から訪問してみたかった店だ。

入ると意外なほど奥に広い造り。俺は初めての訪問だったのでビビッて入口近くに陣取ったが、そこからまだ10人は並んで飲めるくらいカウンターが続いており、奥にはテーブル席もある。これならふらりと立ち寄っても、入れないってことはないな。

客層は若い兄ちゃん、姉ちゃんから、近所の老婆まで幅広い。つーか、普通のサラリーマンは少ないな(笑)

結局、この日は「日々来」で腹いっぱいになっていたので、「ジムビームハイボール」と「小いわし炙り」の計 830円だけで撤収。

まあ、立ち飲み屋なので当たり前なのだが、店員と常連さんの距離が近く、ひっきりなしに常連さんが店員に話しかけているのでなかなか注文もしづらい感じもあった。

こりゃ、再訪はないかな・・・とも少し思ったんだが、帰り際にママさん(かな?)が「また来てくださいね。気軽に寄ってくださいね」と声をかけてくれ、一見客も大事にしてるんだなってのはわかったので、また行ってみるか・・・という気にはなっている(笑)

面白い日本酒も置いてるみたいだし。
6/18(木)。以前大竹のお客さんのところで一緒に働いていたのだが、その後色々あって郷里に帰っていた女子が「今度結婚することになったので最後の独身ひとり旅を楽しみます」って広島にやってきた。

翌日、その大竹のお客さんたちと岩国でその子の結婚のお祝いをすることになってたのだ。

20260618_torau1.jpeg
大竹時代の同僚 LINE に「夕方広島に着いて、広電に乗り間違えて比治山の方まで彷徨って、今、無事稲荷町まで戻って食事を終え、小町のホテルに向かってます。」ってメッセージが入ったのが
20時過ぎ(笑)。「ちょうど今広島にいるから、立ち飲み屋でもよかったら袋町で一杯やる?」と反応すると「ぜひぜひ」ということだったので
NHKビルの前で落ち合って「魚寅」へ。

もう時間も遅かったので二人ともいきなり日本酒。俺は「原田 夏純米 ナツ 無濾過生原酒」を。彼女は・・・なんだっけ?山口県の酒の何か(笑)で乾杯。

「魚寅」はご存じのように(いや、知らんだろうけど)山口県の酒が充実している。彼女も「辛い酒が好き」と言いながら、どちらかというと甘めの山口県の日本酒「おいしい」と楽しんでくれた。

アテは、「せっかくなんで瀬戸内の味を楽しんどく?」と刺身の盛り合わせと広島地物のアナゴ刺しを頼んだ。アナゴを一口食べて「おいしい!おいしい!」と感動していたのでアナゴは全て彼女へ(笑)。

20260618_torau2.jpeg
俺の二杯目は「五橋 純米生酒」。彼女も同じものを。これも「おいしい」と飲んでくれたので、結婚祝いに「五橋」を送ることを約束。岩国の酒を喜んでくれるとうれしいねえ。

お互いの近況報告をしつつ美味い酒と肴を楽しんでいたら、あっという間に時計の針も 23時をまわり、翌日の岩国での飲み会もあるのでお開きに。

彼女には本当に俺らおっさん技術者は色々助けてもらった。「なんか、お客さんがエラー出るって言ってるんだけど、何度やっても再現できないんでどうにかして」と彼女に丸投げして、「再現できましたよ(笑)」「ウソ?(驚)」って救われたことが何度もあった(^^;;;

そうしてわしらが色々迷惑をかけた分も、しっかり幸せになってもらいたいと広島の空に浮かぶ細い月に祈った夜であった(笑)
俺はお好み焼(もちろん広島のね)の麺の端っこはパリパリになっていてほしい。そういう人、多いでしょ?

20260714_hibiki1.jpeg
そんな俺に「この店のお好み焼はマジでパリッパリ」と教えてくださったのは横川・三篠方面在住のお客さんである。
「麺の端がパリパリに焼けてるのが好きなら、絶対満足できます」と。

先週その話を聞いて、早速今日その店を訪ねてみた。
今日はあまりに暑く、袋町から段原南まで真っ昼間に歩いて移動した俺は完全に正気を失っており、「俺、今日はクソ熱いお好み焼き屋の鉄板の前で、冷たい生ビールをゴクゴク飲むんだ」と職場の人たちに宣言し、実際に横川駅で電車を降りた俺はまっすぐその店に向かったのである。

それが「お好み焼き&鉄板焼き 日々来」。もちろん初訪店である。

20260714_hibiki2.jpeg
店に入ると俺が口開けの客だった。勧められるままにカウンター席の端に腰を下ろす。
ポテトサラダを肴に生ビールを飲みつつ、「そば肉玉」が焼けるのをジッと待つ。

そして、いよいよ俺の眼の前にお好み焼が・・・。生ビールはもう飲み干してしまった。「ブラックニッカハイボール」を追加注文し、そして手にもったヘラをお好み焼に差し込む。もう、それだけで手にはパリパリと固く焼けた麺が切れる、いや、折れる感覚が伝わってくる(笑)

マジで、広島一のパリパリ麺だった。
いや、他にも一番はおるかもしれんでって言う人もいるだろうが、これ以上パリパリになったら(そりゃ、そういう店もあるかもしれんけど)俺的にはもうそれは「お好み焼」とは別の料理だと思う。「日々来」のパリパリ度が「美味しく食べれる限界」だと思う(笑)
そういう意味で「広島一のパリパリ麺」と言っていいだろう。

20260714_hibiki3.jpeg
麺をパリパリに焼くためか、ちょっとサイズは小さめで、そこが熟年デブには少しさびしかったが、存分にパリパリ麺を堪能した。

俺のあとに三人連れの高齢のお客さんがやってきたが、この店のお好み焼がパリパリだということを知り、わざわざ車に乗り合わせてきたということだった。なので可哀想に運転手のおじいさんはノンアルを飲んでいた。

そうか、高齢者にも情報が届くほどパリパリ麺として有名なんだ。そしてわざわざ遠方から車でやってくるほどパリパリ麺好きは多いんだなあ(^^;;;

三人の客の一人が少し興奮気味に「うどんもパリパリなの?」と聞いていたが、店主はやや苦笑いで「うどんはパリパリじゃありません」と回答していた。

まあ、うどんほどの太さのものがパリパリだとちょっときついよね(笑)
Oracle DBを使ったシステムで、時間(ミリ秒まで)をキーにしているテーブルで「キーの重複エラー」が出てプログラムが落ちるという障害が出たので、ちょっと調べてみた。

Oracle でミリ秒まで管理する場合のデータ型は DATE ではなく TIMESTAMP となる。精度はナノ秒(秒数以下(小数秒) 9桁)まで。

テストで、TIMESTAMP型の項目をキーとしたテーブルを作成する。

CREATE TABLE t_test1 (
    id INTEGER,
    udate TIMESTAMP,
    PRIMARY KEY (id, udate)
)

id と udate の組み合わせでユニークでないといけないテーブルだ。

なので、

INSERT INTO t_test1 (id, udate) VALUES(1, '2026-07-07 11:00:00.111111');

を 2回実行しようとすると、

ORA-00001: 一意制約(TESTER.SYS_C008227)に反しています

のエラーとなる。

INSERT INTO t_test1 (id, udate) VALUES(1, '2026-07-07 11:00:00.111222');

であれば、ミリ秒以下(小数秒 4~6桁目のマイクロ秒)の値が違うので登録はできる。

ミリ秒(秒数以下 3桁のみ)までの指定での INSERTでは、マイクロ秒に 000 を補完するので、

INSERT INTO t_test1 (id, udate) VALUES(1, '2026-07-07 11:00:00.111');

これも(内部的には 11:00:00.111000 という時間になり)通る。

SELECT 時に書式を指定しなければ、TIMESTAMP の項目はミリ秒までしか表示しない。

SELECT * FROM t_test1 WHERE udate >= '2026-07-07 11:00:00.111';

20260707_timestamp1.jpg

例えばマイクロ秒まで表示したければ、小数行を 6桁表示するようにフォーマットを指定する。

SELECT id, TO_CHAR(udate, 'YYYY-MM-DD HH24:MI:SS.FF6') FROM t_test1
WHERE udate >= '2026-07-07 11:00:00.111';

20260707_timestamp2.jpg

ちなみに、CREATE TABLE 時に TIMESTAMP とだけ指定すると、マイクロ秒(小数秒 6桁)までの管理となる。
そのため、

INSERT INTO t_test1 (id, udate) VALUES(1, '2026-07-07 11:00:00.111222333');

は、実際には 111222 までしか見ないので(ナノ秒部の 333 は無視する)、

ORA-00001: 一意制約(TESTER.SYS_C008227)に反しています

となる。ナノ秒まで扱うのであれば、

CREATE TABLE t_test1 (
    id INTEGER,
    udate TIMESTAMP(9),
    PRIMARY KEY (id, udate)
)

と、TIMESTAMP の後ろに少数桁数の(9)を設定する必要がある。

ところで、最初に書いた障害は、CSVファイルに「ミリ秒まで」書かれたデータを読込み DB に登録するときに発生したもの。元のデータがミリ秒までなので、TIMESTAMP の指定がどうこうで解決する問題ではない。
結局、時間をキーにせず、自動採番した番号などをキーにするしかない。

CREATE TABLE t_test1 (
    num NUMBER GENERATED ALWAYS AS IDENTITY,
    id INTEGER,
    udate TIMESTAMP(9),
    PRIMARY KEY (num)
)

とかね(num はシーケンスを使ってユニークに自動採番される)。

いや、あれこれ TIMESTAMP を試していたのは何なの?って話なんだけど、今後無駄になる知識じゃないからね。勉強、勉強。

ちなみに TIMESTAMP は、PostgreSQL や MtSQL などで微妙に仕様が違うからね。使うときは注意ですぞ(笑)
Oracle Database からデータを取ってくるバッチ処理を Spring Batch 6 で作ろうと思って、Eclipse
で依存関係をこんな風に設定した。

20260702_batch1.jpg

DB関係は、Spring Data JDBC と Oracle Driver を選択した。

pom.xml の中はこんな感じ。以下依存関係が存在することを確認した。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc11</artifactId>
            <scope>runtime</scope>
        </dependency>

Spring Batch(Spring Boot)の勉強始めてから、ぼんやり、Spring Data JDBC(spring-boot-starter-data-jdbc)が設定する環境(最低限の変換アダプタとしての基盤)に、Oracle や PostgreSQL ドライバが各 DBMS 特有の機能を補完する形になっているのかと思ってた。つまり、Spring Data JDBC と実際に使用する DBMS を組み合わせて使うものだと。

でも、今朝、ふと「Oracle JDBC ドライバって言うくらいなんだから、ojdbc11 単体の依存関係設定だけでいいんじゃね?」と思いついて、そもそも Spring Data JDBC ってなんやん?実はいらんのんちゃう?と調べてみると・・・

いらんやん・・・(^^;;;

最低限必要なのは Spring Batch と Oracle Driver だけやん。

上に書いたような「最低限の変換アダプタとしての機能」は spring-boot-starter-jdbc(SpringのJDBC基盤)で作られるんだけど、これは Spring Batch のスターター(spring-boot-starter-batch)が内部的に読み込んでいる。なので、この基盤の上で動く各 DBMS のドライバ(今回は Oracle Driver)だけ依存関係を設定してやればええんや。

必要なのは、

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>

        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc11</artifactId>
            <scope>runtime</scope>
        </dependency>

これだけね。

ちなみに、Spring Data JDBC で設定される機能って何かというと(Qiita の @yoshikawaa(Atsushi
Yoshikawa)さんの記事によると)、

  • CRUDを実現するシンプルなAPIを提供する
  • @Queryでカスタムクエリを定義できる
  • SQL実行前後などのイベントをインターセプトする
  • Spring Data JDBCを経由してMyBatisのSQLを実行できる
  • これらをAuto-Configする

なるほど、依存関係を設定しておくと色々便利なのね。ただ、これらの機能を使用しない、ただ単にDBを読むだけなら必要ない。Oracle Driver だけあればいい・・・という話なのね?

とりあえず残しておこう。

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

月別 アーカイブ

電気ウナギ的○○ mobile ver.

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