2026年8月アーカイブ

土橋のお客さんのところで作業をした帰りは「汁なし担々麺専門店 楽(らく)」で晩飯。

最近、汁なしの二郎インスパイア「森二郎」を食べることが多かったのだが、この日は普通の二郎インスパイア(まあ、普通というのもおかしいが。二郎系そのものが本家も含めてキチガイ料理だからな(笑))「楽二郎」を。

20260803_raku1.jpeg 20260803_raku2.jpeg 20260803_raku3.jpeg

細麺(ディフォルトで大盛りになる)、にんにく多めで注文。1,300円。ああ、けっこういい値段だねえ。ま、その価格に合う内容だと思うので文句はない。

ほれほれ、この見事なにんにくタワー

スープをすすると、追加された生にんにくで舌がぴりぴりする(笑)
食べてるうちにおろしたにんにくの粒たちがどんどん底にたまっていくので、最後まであの濃いスープを飲まざるを得ない。せっかく「多め」で頼んだにんにくを残していくなんてできない!(食品ロス削減!!)

ま、こんな食べ方をしているから身体はどんどん肥満度を増していくわけだが(笑)

そうして俺が幸せな時をすごしている間に、なんと広島市内は突然の雨に。けっこう激しく降っている。空も完全な曇天で、すぐに止むような気配もない。
折りたたみ傘を鞄の中に常備はしているが、風も雨も強い。俺は徒歩で横川まで帰るのは諦め、土橋電停から広電の電車に乗った。

一応「にんにく増しのラーメンを食ったあと」のエチケットとしてマスクはしたのだが、それでも二人ほど俺の口元を見て顔をしかめたやつがいた。失礼しちゃう!マナーがないなあ!!
7/22(水)。久しぶりの「竹乃屋 ミナモア店」。

20260722_takenoya1.jpeg
前回、お代が 2,500円ほどにな完全敗北してから何となく足が遠のいていたのだが、あのお気に入りの「親鶏ふりそで」と「焼さば串」が食べたくなって訪店。
今回はすでに「驛麺屋 BIGFRONTひろしま店」で生ビールとつまみ二皿をやって腹もこなれているからな。前回のような失敗はしないという自信を胸に、意気揚々と入店したのである。

立ち飲みカウンターに立つと、すぐに QR コードを読んで注文画面にアクセス。一杯目だけは店員に注文しないといけないので、注文を取りにきた女の子にジムビームハイボールと伝える。ただ、「早く親鶏、親鶏」と親鶏ふりそで串の注文を焦っていたので、スマホ画面に集中して女の子の方を一度も見ず。失礼な態度だったと反省。店と客は対等な立場だからな。

20260722_takenoya2.jpeg
ま、それはいいんだけど、なんとメニューから「親鶏ふりそで」が消えているではないか・・・マジか?
何度も何度も同じ画面をスクロールさせて確認するのだが、「親鶏ふりそで」の文字はどこにもない・・・

もうひとつのお気に入り「焼さば串」はあったので、とりあえず「とりもも梅肉串」「焼さば串」「生いわし串」を注文。生いわし串は初めて頼んでみたのだが、まあ、なかなかイケる。頭から腹にかけてはちょっと肉がふかふか柔らかすぎるんだが、しっぽの方は身も引き締まっており焼き魚の香ばしさもあって美味い。しかし、親鶏ふりそでの代わりになるほどではない・・・寂しい・・・

そんな寂寥感に苛まれた俺の心の隙間に入り込むように、店の女の子が「今、店で一押しなのが『カツオ白子ポン酢』なんですけど、注文しませんか?」と俺の横にやってきてささやく。そいつは親鶏ふりそでに代わって俺の心を満たしてくれるのだろうか?・・・俺はしばし悩んだ末「カツオ白子ポン酢」を注文したのであった。

20260722_takenoya3.jpeg
カツオの白子はけっこう歯ごたえがある。俺のイメージしていた「とろっとした感触」とは違う。それに白子と言えば濃厚な味を予想するが、なんともさっぱりした味であった。美味いけど、なんか思ってたんと違う・・・

しかも、俺にカツオ白子を勧めた女の子が飲み物を運んできてのだが、俺の前の白子を一瞥しただけで一言もなく帰っていく。「わぁ、私がすすめた白子を頼んでくれたんですねえ」くらいの反応はあるとおじさんは期待していたのに。その言葉で親鶏ふりそでを失った寂しさを埋めようと思ったのに・・・

その後、芋焼酎ハイボールを一杯、芋焼酎のお湯割りを二杯やったが全然酔えず・・・。またしばらく足が遠のくかもしれない・・・

お代は 1,500円ほど。勝った・・・リベンジは果たした。しかし俺の心には「喜び」の感情はまったく湧いてこなかった。
金曜日は午後休を取ったので、昼飯は紙屋町パセーラ地下の「中国麺飯店 万豚記(ワンツーチィ)」で「担々麺(赤)+肉めしセット」1,320円をチョイス。

20260807_wantsuti01.jpeg
本当は横川の「味億」で尾道ラーメンを食べようと思ってたんだけど、なんと金曜日が定休日だと判明(^^;;
「え?マジ?」とショックのあまりバスの中でちょっと失禁してしまったけど、そう言えばパセーラの地下にも尾道ラーメンの店が・・・いや、その隣に前から気になっていた担々麺が売りの中華屋があったなと思い出し紙屋町でバスを降りたのだった。

俺を含め2組待ちだったけど回転は意外に早くてすぐに入店できた。多分調理が追いつかないからか、席の三分の一ほどは使っていないようだった。実際、麺類はすぐ出てきたのだが、餃子は時間がかかっていた。

この店は黒担々麺が売りの店のようだが、黒って黒胡麻でしょ?あんま好きじゃないのよね(笑)。なので上記のように赤を選択。

その担々麺(赤)はさすがちゃんとした中華の店の料理。ちゃんと辛くてそこに複雑な香辛料の味が絡む。以前業務スーパーで買った鍋の素みたいな味や(そこまで辛くないけど(笑))

20260807_wantsuti02.jpeg
この店の味は気に入ったので、季節限定メニューの「冷やし黒胡麻担々麺」ってやつを今度試してみたいな。

「黒胡麻は嫌いって書いてるじゃん!」って?いや、正確には「あんま好きじゃないのよね」だ。決して好きではないのだけど、これはちょっと挑戦してみてもいいかなあと。「汁なし」じゃないんだけど、麺の半分くらいのところまでしか汁が入ってなくて・・・ああ、「冷やし中華」スタイルか。

なにせ段原勤務でなかなか紙屋町方面は帰宅途中に寄れる場所じゃないんだけど、そういえば職場から広島県庁までの無料のシャトルバスも出てるし、近々に再訪だ。

夏は誰もが痩せる季節なんだけど、俺はまだまだ太るよ!!応援して!!
Spring Boot 4 で作成したファイルダウンロード画面。「ダウンロード」ボタン押下で DB を読んで CSV ファイルを生成し、そのファイルがクライアントPCにダウンロードされる。

Controller はこんな感じ。

    // ボタン押下で実行される処理
    @PostMapping("/createFile")
    public ResponseEntity<Resource> downloadFile() throws Exception {
        Path path    = HogeService.make_file_path(); // ダウンロードされるファイルを作成するパス
        HogeService.logic_make(path.toString());     // ダウンロードファイルを作成

        Resource resource    = new FileSystemResource(path);

        HttpHeaders headers    = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"");
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);

        return ResponseEntity.ok()
                .contentType(getContentType(path))
                .contentLength(resource.contentLength())
                .headers(headers)
                .body(resource);
    }

    // ContentType の取得
    private MediaType getContentType(Path path) throws IOException {
        try {
            return MediaType.parseMediaType(Files.probeContentType(path));
        }
        catch (IOException e) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
    }

これで、例えば

    <form action="/createFile" method="post" id="form" name="form">
        <input type="submit" name="download-btn" class="btn-primary" id="download-btn" value="ダウンロード">

みたいな形にすれば、「ダウンロード」ボタン押下でブラウザのダウンロード窓が開いてダウンロードは行われる。

しかし、例えばダウンロードに失敗したときは Web 画面上にエラーメッセージを表示したいとか、成功したら画面に「正常にダウンロードできました」などと表示したい場合はこれではダメだ。

Controller の各メソッドで返却できる値は 1種類である。
上の例のようにデータ(ダウンロードファイル)を出力するか(ダウンロード)、HTML を出力するか(画面表示)、生のテキストや JSON データなどを出力するか(API など)・・・のいずれかひとつのことしかできない。
「先にファイルを出力して、その次に結果を伝える HTML を出力する」という二段構えの出力はできない。ファイルを出力すれば画面(HTML)は表示できないし、画面(HTML)を表示(出力)すればファイルは出力できない。

・・・が、ファイルダウンロードをして結果を Web 画面に表示したい要件ってあるよねえ。

そういうときは、直接ファイルダウンロードの画面を呼び出すのではなく、JavaScript の中でファイルダウンロード画面を呼び出し一旦ファイルを受信しするようにする。そして、そのデータをクライアントに出力し、その後画面の編集をすればいい。JavaScript で処理すれば「ファイル出力か HTML 画面表示かどちらかしかできない」なんて制限ないからね。

具体的には、↓こんな感じの JavaScript。

    document.getElementById('download-btn').addEventListener('click', async (e) => {

        e.preventDefault();

        const btn    = document.getElementById('download-btn');
        const errMesg = document.getElementById('error_msg');
        const sucMesg = document.getElementById('success_msg');

        errMesg.style.display = 'none';  // メッセージ非表示
        sucMesg.style.display = 'none';  // メッセージ非表示

        try {
            btn.disabled = true;  // ボタンが押されたら、二度押し防止のため非活性化

            // ダウンロード処理呼び出し
            const action_url    = /*[[@{/createFile}]]*/ '';
            const response = await fetch(action_url, {  // fetch(非同期通信(HTTPリクエスト))でダウンロードデータ取得
                method: 'POST'
            });

            if (!response.ok) {  // ダウンロード画面からデータが取れなければ例外発生
                throw new Error()
            }

            const blob = await response.blob();  // データをBlob(バイナリの塊)として取得

            const disposition  = response.headers.get('Content-Disposition') ;  // レスポンスヘッダーからファイル名取得
            let filename = 'download.csv';  // ファイル名が取れなかったときのディフォルト値

            if (disposition && disposition.indexOf('attachment'!== -1)) {
                const fileMatch        = /filename="?([^; \n"]*)"?/.exec(disposition);  // 正規表現でファイル名取得
                if (fileMatch && fileMatch[1]) {
                    filename    = fileMatch[1];
                }
            }

            const url  = window.URL.createObjectURL(blob);  // ダウンロード用の疑似リンク作成
            const a    = document.createElement('a');     // 疑似アンカータグ作成

            a.href     = url;  // アンカータグの href 属性に疑似URLを紐づける
            a.download = filename;  // アンカータグの download 属性にファイル名をセット
            document.body.appendChild(a);  // 作成した疑似アンカータグを一時的に(操作のため)実体化
            a.click();  // 実体化したアンカータグを画面の前の人間に代わってクリック(ダウンロードスタート)

            a.remove();  // もう必要ないのでアンカータグ削除
            window.URL.revokeObjectURL(url);  // メモリに残った疑似リンク(blobデータ含む)を削除

            btn.disabled = false;  // ボタン使用可能に
            sucMesg.textContent = "正常にダウンロードが終了しました。";
            sucMesg.style.display = 'block';  // メッセージをセットして表示

        }
        catch(error) {
            btn.disabled    = false;  // ボタン使用可能に
            errMesg.textContent = "ダウンロードに失敗しました。";
            errMesg.style.display = 'block';  // メッセージをセットして表示
        }
        finally {

        }
    });

ダウンロードボタンが押されたらイベントリスナーによりこの処理が実行される。
コメントに書いているとおり、JavaScript 内で(画面の前の人間には見えないところで)実際のダウンロード画面にアクセスしデータを取得。そのデータをダウンロードするための疑似リンクを作って、そのリンクに(画面の前の人に代わって)アクセスして画面の前の人のパソコンにデータがダウンロードされるという動き。

なんでこういうことをするかというと、JavaScript 内でダウンロードの成否を判断して処理を行うため。

ダウンロードに失敗したときは例外を発生させ「失敗」メッセージを表示。最後まで問題なく処理が終われば「正常」メッセージを表示・・・となる。(直接ブラウザでダウンロードをした場合は、成否の情報は画面(JavaScript)には伝わってこないので何もできない。)

Controller のファイルダウンロードのメソッド(上の例では downloadFile メソッド)を修正する必要はない。
CSV を読み込んで DB に登録している処理で、今は単純に INSERT を行っているのだが、それだとキーが重複したときに

Caused by: java.sql.BatchUpdateException: バッチ 0 INSERT INTO t_user_summary (
    user_id,
    user_name,
    user_address1,
    user_address2,
    user_phone_number,
    anniversary,
    note
) VALUES (
    ('1'::int8),
    ('MASA SHIMURA'),
    ('岩国市黄金町黄金1928-11'),
    (''),
    ('090-0000-1111(携帯)'),
    ('19650824'),
    ('誕生日(2回目)')
)
 はアボートしました: ERROR: 重複したキー値は一意制約"t_user_summary_pkey"違反となります
  詳細: キー (user_id, anniversary)=(1, 19650824) はすでに存在します。 このバッチの他のエラーは getNextException を呼び出すことで確認できます。

このように更新に失敗する。(このケースは、user_id が '1' で anniversary が '19650824' のデータがすでに存在している)

そこで、まず存在チェックを行って、存在していれば UPDATE、存在していなければ INSERT を行うようにしたい。
そのために、SELECT 処理を埋め込みたいが、Spring Batch の Chunk 方式の場合、どこにその処理を入れればきれいなのか?と Gemini に気いてみたら「SQL 直せばいいだけです」と言われた。

そうなん?いやあ、俺も、SQL をきちんと(専門書を買うなどして)一から勉強したことなくて・・・。長年の経験から相当複雑な SQL も組めるけど、あるステートメントのオプションなんかは知らないものもちょこちょこある。

Gemini が示した SQL の、

INSERT INTO t_user_summary (
                user_id,
                user_name,
                user_address1,
                user_address2,
                user_phone_number,
                anniversary,
                note
            ) VALUES (
                :user_id,
                :user_name,
                :user_address1,
                :user_address2,
                :user_phone_number,
                :anniversary,
                :note
            )
            ON CONFLICT (user_id, anniversary) DO UPDATE SET
                user_name = EXCLUDED.user_name,
                user_address1 = EXCLUDED.user_address1,
                user_address2 = EXCLUDED.user_address2,
                user_phone_number = EXCLUDED.user_phone_number,
                anniversary = EXCLUDED.anniversary,
                note = EXCLUDED.note

ON CONFLICT () DO もそうだし、EXCLUDED も何?って感じ(笑)

ON CONFLICT () DO は「キー重複時の動きを指定する PostgreSQL 独自の句(オプション)」なんじゃね(SQLite でも書き方は違うが使える)。EXCLUDED は「INSERT に失敗したデータが一時的に保管されている DB」を表すんだそうな。

もう、各 RDBMS 独自の構文になってくるとわけわからん(笑)。なもんで、この業界には「Oracle しか使えん」っていう Oracle 独自構文にどっぷり浸かって抜け出せない Oracle じいさんがたくさん存在してるけどな(笑)

というわけで、ItemWriter 内の SQL を上記に置き換え。

test01=> SELECT user_id, anniversary, note FROM t_user_summary WHERE
user_id=1 AND anniversary='19650824';
 user_id | anniversary |  note
---------+-------------+--------
       1 | 19650824    | 誕生日
(1 行)

が、

test01=> SELECT user_id, anniversary, note FROM t_user_summary WHERE
user_id=1 AND anniversary='19650824';
 user_id | anniversary |       note
---------+-------------+------------------
       1 | 19650824    | 誕生日(2回目)
(1 行)

と、ちゃんと更新されていてバッチグー(死語)であった。
週末も仕事って日が続いてて、この土曜日もやっぱり仕事だったんだけど、やっと日曜日オートバイに乗る時間ができた。午前中、親父の介護アパートに新聞を届けた足でそのまま川上から川越方面に愛機 DUCATI MONSTER S2R 1000 を走らせた。

結局、県道5号線(自動車は離合が大変な険道(けんどう)である)を美川町まで走り、「観音水車 でかまるくん」を観て帰った。行程往復60kmほどのプチツーリングである(笑)

20260802_dekamaru1.jpeg 20260802_dekamaru2.jpeg

「でかまるくん」・・・名前が(^^;;; もうちょいええ名前を考えりゃあええのに。地元の小学生に名前を公募した感じかな?

平成2年(1990年)に造られたというから(当時は「美川町の大水車」と呼んでたようだが)、もう 36年の歴史あり?でも、長い間周りの休憩所なども含め放置されてたみたい。

2年くらい前まで活動されていた美川町の地域おこし協力隊の人の SNS で復活までの苦労話を読んでいたので、ちゃんとぐるぐるまわるでっかい水車を眺めながら、「これがあの・・・」と若干感慨を抱く・・・

凄い坂なので、初心者ライダーはバイクを停めるときやUターン時に「立ちゴケ確率」78%くらいの場所だけど、直径12mの水車はなかなか迫力あるので、一度観てみてはどうでしょう?(立ちゴケしても責任はとりませんが(笑)
久しぶりのビアガーデン。

何年も前に一度岩国の人気のビアガーデンに某秘密結社のメンバーと行ったけど、それももう20年ぶりのビアガーデンって感じだったと思う。

お客さんのところの慰労会に参加させてもらって、その会場が「そごう屋上ビアガーデン 海の恵屋」だったのだ。
そごうのビアガーデン、行ったことあるけど 30数年前だな(笑)。まだビアガーデンやってたのか(笑)

20260731_sogo1.jpeg
20代のころはよくビアガーデン行ってたなあ。3,500円で飲み放題とかそんな感じだったよね。若い頃はガブガブ酒も飲んでたし(俺の若い頃は日本酒が不味くて(^^;、ビールオンリーだったけど(笑))、食う量も凄かったので余裕で元が取れてた気がするが、さすがに今は食べ放題・飲み放題にそんなに魅力を感じないお年頃になったので、ビアガーデンは選択支にはいることもなくなったよねえ・・・

いや、マジでこういう会でもないとなかなか機会がないんで、慰労会に誘っていただいたのはありがたかった。
外注の俺らにも補助金を出していただいたので、男性 5千円(ビアガーデンの値段も上がったなあ)のところが団体割引なども含め一人 1,800円で済んだのにで、元を取るために必死で飲み食いする必要もなく(笑)、ゆっくり落ち着いて飲み食いできました。

ビール&焼酎ソーダ割りを 7~8杯、料理を大皿に 3~4回おかわりしたくらいかな。
・・・5千円でも元取ってるな(笑)
先月2日の火曜日の朝、二ヶ月ぶりの「立ち喰い 辰屋」。横川商店街の立ち食いうどん屋だ(横川駅構内には立ち食いうどん無いんでね)。

20260602_tatsuya1.jpeg
ちなみに、西日本ではそばよりうどんの方がよく食べられているので、「立ち食い」ときたら「そば」ではなく「うどん」だ。なので、辰屋も「立ち食いうどん屋」と呼ぶ。「立ち食いそば屋って言うんじゃないのか?」と思った関東人よ、それは東京の常識。広島じゃ通用せんのじゃ!!はぁはぁはぁ・・・

すみません。興奮してしまいました。

で、この日は高森高校の学校運営協議会があるので仕事を休んで岩国に朝帰り。となると辰屋で朝ごはんよね。反対に、朝帰りのときでもないと、なかなか辰屋による機会がない(^^;;;

注文するのはいつものやつ。「天ぷらうどん」と「おにぎり(しゃけ)」。

お、なんだ?おにぎりがいいじゃん!この間は、三角むすびの辺に沿って海苔が巻かれていたが、今回はしっかりおにぎり全体が海苔に巻かれてるぞ。こういうのが良いのよ。

讃岐の人が発狂しそうなやわらかい麺と優しい出汁(関西風)のうどんに海苔のおむすび。ああ、幸せな朝食。
Eclipse で GitHub Copilot のプラグインを使おうと思ったのだが、チャットに質問を投げてもタイムアウト(AI モデルが違うみたいに言われる?)して全然使えない。

・・・いや、実はもう解決してしまったので、正確なエラー状況がわからなくなっちゃったのよ。
ちゃんと調べるためにメッセージとか取っておこうと思ってた矢先に Eclipse の Update に失敗して Eclipse 自身が動かなくなっちゃって・・・で、最新の Eclipse をインストールしたら使えるようになっちゃった(笑)

20260730_chat1.jpg

いや、Gemini や Copilot とかから「プラグインが古いのかも。新しいものに入れ替えたらうまくいきませんか?」と言われていたのだが・・・

<Copilot の回答>
ログをありがとうございます。スタックトレースだけ見ると「チャット応答生成の途中で失敗した(Turn step failed)」という一般的な例外で、原因は主に次のどれかです:

  • Eclipse Copilot プラグインが要求した「モデル」がアカウントで使えない(= GitHub 側で許可/課金が必要)
  • 認証/トークンの問題でモデル呼び出しが失敗している(認証エラーやトークン期限切れ)
  • ネットワーク/プロキシの制約でモデルサービスへ接続できない
  • プラグイン側(バグや古いバージョン)で例外が発生している

使いたいモデルが許可されてること(Freeだし(笑))、認証情報に問題ないこと(Web版は使えてるからね)、ネットワークにも問題ないこと(だって、Copilot Chat が使えないだけで、ソースの自動補完をしてくれる Inline suggestions は使えてたし)は確認できてて、あとはプラグインの更新か・・・と思ってたところなのよ。

Pleiades All in One ダウンロードサイトから最新の Java Developers Edition を落としてきて入れ替えた(GitHub Copilot のプラグインも自動でセットされる)だけなので、プラグインの更新が効いたってことだろうなあ。
何もせず新しい Eclipse 起動したら使えるようになっとったわ(笑)

ちなみに、今入っているバージョンは以下のとおり。

Eclipse IDE for Java Developers Version 2026-06 (4.40.0)
GitHub Copilot Version 0.19.0.202606240339

以前のバージョンも 2026-03 版だったので決して古いものではなかったが、なんか GitHub 側で最近仕様変更とかあったんかねえ?

このアーカイブについて

このページには、2026年8月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2026年7月です。

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

月別 アーカイブ

電気ウナギ的○○ mobile ver.

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