torutkのブログ

ソフトウェア・エンジニアのブログ

Java読書会「Spring徹底入門 第2版」を読む会(第15回)を終えて

Java読書会BOF 主催「Spring徹底入門 第2版」を読む会(第14回)を12月20日(土)に開催しました。

今回は、12章 Thymeleafの残りと14章チュートリアルの途中、エンティティの実装まで読み進めました。14章のチュートリアルは、参加者がそれぞれ持参のPCで実際にPostgreSQLのユーザー・データベース作成、Spring Bootのプロジェクトを作成してソースコードの打ち込みをしながら進めました。

チュートリアルにおいて引っかかったことなど

実際に手を動かしながら実施すると、書籍を読むだけでは気づかないことや引っかかる事項がいろいろと出てきます。これらを調べ議論しながら進めたことで知識が深まり知見が広がりました。

Spring Framework、Spring Bootのバージョン

先月2025年11月に、Spring Framework 7.0 および Spring Boot 4.0がリリースされました。 書籍で言及されているSpring Bootの構築は、WebサービスSpring Initializr を利用して作成します。Spring Initializrで選択可能なSpring Bootバージョンは、最新バージョンと一つ前のマイナーバージョンで本日時点では 4.0.1または3.5.9 となっています。書籍のチュートリアルは Spring Boot 2.7を前提としていますが、事前に試した限りでは 3.5でも 一部修正をすればビルド・実行は可能でした。

修正:依存関係でthymeleaf-extras-springsecurity4 をthymeleaf-extras-springsecurity6 に修正

PostgreSQL ユーザー作成時のパスワード指定

書籍では、次のように mrs ユーザーを作成するPostgreSQLコマンドで、パスワードにmd5ハッシュコード化したものを指定しています。

CREATE ROLE mrs LOGIN
  ENCRYPTED PASSWORD 'md586082399b5082acb54472ee195a57ce8' ...
                                                 

PostgreSQLは、バージョン10あたりから ENCRYPTEDの指定は意味をなさなくなり(後方互換性のために残されている)、パスワード文字列がハッシュコードの場合はそのまま(再ハッシュせずに)パスワードとして保存されます。md5ハッシュを使用するときは、先頭にmd5を付け、その後ろにmd5ハッシュ文字列を指定します。指定可能なハッシュは、md5およびSCRAM-SHA256です。

ここで発生した問題は、書籍ではユーザーのパスワードを mrs と記載しているのですが、上述のCREATE ROLE文で指定しているパスワードのmd5ハッシュ文字列が、mrsをmd5ハッシュ化したものと一致しないことでした。書籍の通りユーザーを作成すると認証ができません。 世の中には、md5のハッシュから元の文字列を逆生成するWebサイトがあり、書籍のmd5ハッシュを入れたところ、mrsmrs と結果が十秒足らずで得られました。md5はかなり脆弱ですね。

ところで、PostgreSQLサーバーとはSSH接続でコマンドを実行するなら、パスワードを平文で指定してもいいのかなと思いましたが、この関連でインターネットを調べていたときに、コマンド実行履歴(history)で見れてしまうという点を見かけ、なるほどと思いました。とはいえ、平文をハッシュにするときをどうするか、もあるので平文パスワードを扱う場所をどこに限定するかは別途検討すべき事項と思いました。

LC_COLLATE と LC_CTYPE

データベースを作成するときのパラメータで、書籍ではどちらも "C" を指定していました。 LC_COLLATEは、文字列の並び順(照合順)を決める設定で、Cは、Unicodeの文字(コードポイント)のバイト順で判断するが、高速に動作し、ja_JP.UTF-8 を指定すると日本語の文字も考慮された並びとなるが、動作が遅くなるという模様です。

LC_CTYPEは、文字の種類を分類するための設定で、文字、数字、大文字、小文字などの区別をするものになります。Cは、ASCII文字範囲(英字)を分類します。

テーブル名 usr

なぜかテーブル名に usr と省略形を使っています。userではいけないのかな? と疑問に思いました。 これは、userとしてしまうと、SQL予約語(CREATE USER ... のUSER)と衝突してエラーになってしまいます。 推奨の回避方法は、名前をuser以外にする(推奨は複数形のusersですが)、あまりよくない回避方法は"user"とクォートする、ただしこの場合、JPAが生成するSQLでもuserをクォートさせる設定を別途行う必要があり、筋が悪い回避方法となってしまいます。

複合キーのクラスReservableRoomId でequalsとhashCodeのオーバーライド

複合キーを使用するエンティティ(テーブル)で、複合キーを表すクラスは包含するキーの値が同じ場合はインスタンスが違っていても同値とする(equalsメソッドがtrueを返し、hashCodeメソッドが同じ値を返す)実装が必要です。

書籍では、次のようなequalsメソッドとhashCodeメソッドの実装をしています。

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ReservableRoomId other = (ReservableRoomId) obj;
        if (reservedDate == null) {
            if (other.reservedDate != null) {
                return false;
            }
        }  else if (!reservedDate.equals(other.reservedDate)) {
            return false;
        }
        if (roomId == null) {
            if (other.roomId != null) {
                return false;
            }
        } else if (!roomId.equals(other.roomId)) {
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((reservedDate == null) ? 0 : reservedDate.hashCode());
        result = prime * result + ((roomId == null) ? 0 : roomId.hashCode());
        return result;
    }

Javaでは、同値の判定をするオブジェクト(値オブジェクト)を実装する場合は、equalsとhashCodeをオーバーライドして同値判定の実装をするのが定石です。

IDEIntelliJ IDEAを使っていたので、試しに IntelliJのコード生成機能で equalsとhashCodeメソッドの実装を生成したところ、次のようなコードとなりました。

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        ReservableRoomId that = (ReservableRoomId) o;
        return Objects.equals(roomId, that.roomId) && Objects.equals(reservedDate, that.reservedDate);
    }
    @Override
    public int hashCode() {
        return Objects.hash(roomId, reservedDate);
    }

java.util.Objectsクラス(JDK 7から導入されたユーティリティクラス)の equals(Object, Object)メソッドとhash(Object...)メソッドを使って、とっても簡潔な実装となっています。

読書会参加メンバーでEclipseを使用している人に、同様に equalsとhashCodeのメソッドをコード生成してもらったところ、java.util.Objects を使用せず、書籍と同じような長いコードが生成されていました。

ということで、IntelliJ IDEAが コード生成機能をきちんと洗練していることが分かりました。

schema.sql

Spring Boot で、application.propertiesに、spring.sql.init.mode=always と指定すると、Spring Bootアプリケーションを起動したときに、src/main/resources/ ディレクトリの下にある schema.sql ファイルと data.sql ファイルを実行します。

schema.sqlには、DDL文を、data.sql には初期データをインサートするSQLを記述します。 書籍では、schema.sql の中に、外部参照の制約をCONSTRAINTで定義しますが、CONSTRAINTに、ハッシュのような長い文字列が付いています。

ALTER TABLE reservable_room ADD CONSTRAINT FK_f4wnx2qj0d59s9tl1q5800fw7 FOREIGN KEY (room_id) REFERENCES meeting_room;

これはどうやら Hibernateスキーマ生成ツール hbm2ddl がJPAアノテーション付きエンティティからSQLDDL文を生成しているもののようです。

シンプルに外部参照を指定するなら、CREATE TABLEの中に記述することもできます。

CREATE TABLE reservable_room (
    room_id INT4 NOT NULL REFERENCES meeting_room (room_id),
    :

schema.sql と data.sql に記載のSQL文は、1文ずつJDBC経由でデータベースに投入されているようです。

読書会の冒頭、アフターでの話題

パッケージ構成(ディレクトリ構成)

Spring のパッケージ構成は、書籍のサンプル・チュートリアルのように レイヤーの観点でまずパッケージを構成し、それぞれの中で必要に応じて機能ごとのサブパッケージを構成する方法(package by layer)と、アプリケーションの業務(機能)の観点でパッケージを構成、それぞれの中で必要に応じてレイヤーごとのサブパッケージを構成する方法(package by feature)とがよく適用されています。

src/main/java/com/torutk/hello
  +- controller/
  |    +- login/
  |    +- search/
  |    +- edit/
  +- model/
  +- service/
  +- repository/

レイヤー観点の分割が上位のパッケージにあると、一つのアプリケーションを構成するソースファイルがかなり広範囲に散らばるので、まとまりという点では機能観点での分割が良さそうに思いました。

T.B.D.

MavenXMLで設定を書かなくても良い

pom.xmlは、XMLなので人の目には少し厳しい(タグの記述がコンテンツよりも目立つ・多い)ですが、最近(しばらく前から?)はXMLではなくYAML形式のpom.yml で記述できるようです。

Maven使っている読書会参加者も知らなかったようです。 Gradleを使っているので、どれだけ利便性が向上するかは分かりませんが、反応はよさそうでした。

Redmineのバージョンアップ(6.0→6.1)作業メモ

はじめに

Linuxサーバー(Rocky Linux 8)上で、Unicornアプリケーション(Rack)サーバーとNginx Webサーバーを用いた環境でRedmineを稼働しています。

この環境での Redmineバージョンアップ手順と問題対処などをメモしておきます。

Redmineのバージョンは、セマンティックバージョンに基づいて3桁の数値で指定されます。

バージョン番号の付与 - ソフトウェアエンジニアリング - Torutk

バージョンアップの際、どの桁の数値が上がるかで作業が変わります。今回は、稼働中のRedmine 6.0.2を先月リリースされた Redmine 6.1.0にバージョンアップするので、2桁目のマイナーバージョンが上がるマイナーバージョンアップとなります。

アップデート作業

Rubyバージョンの更新

Redmine 6.1のリリース情報から、Rubyの対応バージョンを確認します。Ruby 3.2、3.3および3.4に対応とあります。現在稼働しているサーバーのRubyバージョンを調べると

~$ ruby -v
ruby 3.1.7p261 (2025-03-26 revision 0a3704f218) [x86_64-linux]

残念ながら、対応バージョンから外れていました。というかRuby 3.1自体が 2025年3月でEOL(サポート終了)となっていました。

Rocky Linux 8で対応しているRubyのバージョンを確認すると、次のとおり Ruby 3.3を入れることが可能です。

~$ dnf module list ruby
Name  Stream   Profiles    Summary                                              
ruby  2.5 [d]  common [d]  An interpreter of object-oriented scripting language 
ruby  2.6      common [d]  An interpreter of object-oriented scripting language 
ruby  2.7      common [d]  An interpreter of object-oriented scripting language 
ruby  3.0      common [d]  An interpreter of object-oriented scripting language 
ruby  3.1 [e]  common [d]  An interpreter of object-oriented scripting language 
ruby  3.3      common [d]  An interpreter of object-oriented scripting language 

ヒント: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Rubyバージョンを変更すると、Rubyのgemパッケージ群も更新が必要になります。

まず Rubyの更新を実施します。

 ~$ sudo dnf module reset ruby -y
   :
~$ sudo dnf module enable ruby:3.3 -y
   :
 ~$ sudo dnf install ruby
   :

rubyに関係するOSパッケージの更新を実施します。

 ~$ sudo dnf update

Redmineの更新

更新前のRedmine 6.0は、git cloneにより /var/lib/redmine-6.0-stable に展開しています。Redmineの更新方法としては、cloneしたローカルディレクトリ上でブランチを6.1に切り替えるか、あらたに ブランチ6.1をgit cloneするかの方法があります。 今回は、Rubyも更新しているので、新たにgit cloneする方法とします。

~$ sudo git clone -b 6.1-stable https://github.com/redmine/redmine.git /var/lib/redmine-6.1-stable
  :
~$ sudo chown -R redmine:redmine /var/lib/redmine-6.1-stable/

新たにcloneしてきたので、設定ファイルをコピーしてきます。

redmine-6.1-stable$ cp ../redmine-6.0-stable/config/database.yml config/
redmine-6.1-stable$ cp ../redmine-6.0-stable/config/configuration.yml config/
redmine-6.1-stable$ cp ../redmine-6.0-stable/config.ru .
redmine-6.1-stable$ cp ../redmine-6.0-stable/config/unicorn.rb config/
  • config/routes.rb は、後で修正

Redmineが必要とするgem群をインストールします。

redmine-6.1-stable$ bundle config set --local path vendor/bundler
redmine-6.1-stable$ bundle config set --local without 'development test'
redmine-6.1-stable$ bundle config set --local force_ruby_platform true
  • config set で、/var/lib/redmine-6.1-stable/.bundle/config に設定が書かれます。
  • Nokogiri gemのインストールはデフォルトでglibc 2.29へのリンクを含みますが、Locky 8はglibc 2.28のためリンクエラーとなります。そこでgem インストール時にビルドを行うよう設定しています。

gemパッケージのインストールにネイティブビルドをするようになったら、commonmarker gemのインストールがエラーとなってしまいました。clang関係パッケージが必要となっています。

~$ sudo dnf install clang
  :
redmine-6.1-stable$ bundle install
  :

セッションデータ用クッキーの暗号化キー生成

redmine-6.1-stable$ bundle exec rails generate_secret_token

稼働中のRedmineを停止

~$ sudo systemctl stop redmine-unicorn

データベーススキーマの更新

redmine-6.1-stable$ bundle exec rails db:migrate RAILS_ENV=production

Unicornインストール

/var/lib/redmine-6.1-stable/Gemfile.local を作成

gem "unicorn"
gem "unicorn-worker-killer"
redmine-6.1-stable$ bundle update
~$ sudo ln -s /var/lib/redmine-6.1-stable /var/lib/redmine

/usr/lib/systemd/system/redmine-unicorn.serviceの修正

- WorkingDirectory=/var/lib/redmine-6.0-stable
+ WorkingDirectory=/var/lib/redmine-6.1-stable

- PIDFile=/var/lib/redmine-6.0-stable/tmp/pids/unicorn.pid
+ PIDFile=/var/lib/redmine-6.1-stable/tmp/pids/unicorn.pid
~$ sudo systemctl daemon-reload

添付ファイルの格納ディレクトリの移動

redmine-6.1-stable$ rm -rf file
redmine-6.1-stable$ mv ../redmine-6.0-stable/files/ .

プラグインのインストール

プラグインを各リポジトリからクローン

redmine-6.1-stable$ cd plugins/
plugins$ git clone https://github.com/agileware-jp/redmine_banner.git
  :
plugins$ git clone https://github.com/agileware-jp/redmine_issue_templates.git
  :
plugins$ git clone https://github.com/process91/redmine_latex_mathjax.git
  :
plugins$ git clone https://github.com/haru/redmine_theme_changer.git
  :
plugins$ git clone https://github.com/haru/redmine_wiki_extensions
  :
plugins$ git clone https://github.com/tkusukawa/redmine_wiki_lists.git
  :
plugins$ git clone https://github.com/onozaty/redmine-view-customize.git
  :
plugins$  git clone https://github.com/torutk/redmine_cozy_wiki_macros.git
  :
plugins$ git clone https://github.com/torutk/redmine_glossary.git
  :

必要なgemをインストール

redmine-6.1-stable$ bundle update
  :

データベースのマイグレーション実施

redmine-6.1-stable$ bundle exec rails redmine:plugins:migrate RAILS_ENV=production

テーマのインストール

redmine-6.1-stable$ git clone https://github.com/farend/redmine_theme_farend_bleuclair.git themes/bleuclair

デフォルトのページ設定

config/routes.rb

-  root :to => 'welcome#index'
-  root :to => 'welcome#index', :as => 'home'
+  root :to => 'wiki#show', :project_id => 'swe', :as => 'home'

Java SE 24/JDK 24のリリース

JDK 24のリリースと耐量子暗号

2025-03-18に、JDK 24がリリースされました。半年毎の定期バージョンアップで、非LTSです。 JDK 24

ざっと新機能を眺めてみると、Quantum-Resistant で始まる機能が2つほど正式採用されていました。

  • Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism (JEP 496)
  • Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm (JEP 497)

なんだろうなと思いつつ、JEPのサマリを読んでもピンときませんでした。そんな時、日経NETWORK 2025年3月号の包装を開き、ペラペラ読んでいたら、特集記事「耐量子暗号は正解か 暗号の2030年問題」に、RSA-2048の移行先として耐量子暗号が候補に上がっており、JEPのサマリに登場した略語 ML-KEM、ML-DSA が出てきました。

記事を要約すると

現在の暗号方式であるRSAECCは暗号方式として素因数分解や楕円上の離散対数に基づいpており、今後量子コンピュータの性能向上で解読されることが予想されています。そこで、量子コンピュータでも解読が難しい耐量子暗号として、格子暗号、符号暗号、多変数多項式暗号に基づく暗号方式の標準化をNISTが進めており、2024年8月に鍵交換・データ暗号化に使うML-KEM、デジタル署名に使うML-DSA、SLH-DSAの3つが標準化されました。残るデジタル署名のFN-DSAはまだ標準化が公開されていません。

ここで登場する ML-KEMがJEP 496、ML-DSAがJEP 497でJDK 24に取り入れられたということです。

ただ、記事では、耐量子暗号の安全性の検証に十分時間がかけられておらず、今後脆弱性が見つかる可能性も指摘されていました。

Java読書会「Spring徹底入門 第2版」を読む会(第1回)を終えて

Java読書会BOF主催「Spring徹底入門 第2版」を読む会(第1回)が10月20日(日)に開催されました。

www.shoeisha.co.jp

前回の書籍(基礎からのサーブレット/JSP入門 第5版)は、参加人数が2-3人と少し寂しい読書会でした。今回は7人の参加で開催となり、前回の書籍の読書会に比べると賑わった会となりました。

Springの書籍

今回の課題図書となった「Spring徹底入門 第2版」(2024年5月刊)のほかに、類書で「プロになるためのSpring入門 ――ゼロからの開発力養成講座:書籍案内|技術評論社(2023年7月刊)」があります。書籍の対象とするSpring Frameworkのバージョンが、「Spring徹底入門 第2版」が5系 である一方「プロになるためのSpring入門」が6系となっています。

書籍のボリュームは、「Spring徹底入門 第2版」が752ページに対して「プロになるためのSpring入門」が384ページとほぼ倍のボリュームとなっています。

目次構成を見ると、「Spring徹底入門 第2版」は、Springの概要紹介の薄い1章の後は、2章でDIとAOPの詳細な説明が開始され、頭から読んで手を動かしながら徐々に詳しく解説していくる体裁ではなく、リファレンス的な構成になっています。開発環境についてはAの付録で記載、Hello World的なサンプルは13章の Spring Bootの章で初めて出てきます。 「プロになるためのSpring入門」は、第1部基本編と第2部詳細編と2部構成で徐々に詳しく解説していくスタイルのようです。

入門としての読みやすさは「プロになるためのSpring入門」で、独学でも読めそうです。一方、読書会向き(議論が活発になる書籍)ということではSpring徹底入門なのかなとの感触です。入門本で700ページ越えもサイズが強烈です。

PDFの入手

書籍サイトでは電子書籍の購入で、一見PDF版が見当たらず、KindleKobo、Hontoに誘導されるように見えますが、PDF版を翔泳社から購入可能です。今回、電子書籍購入して参加の方の多くが、PDF版がないと思いKindle版を購入したと言っていました。次の図の操作でPDF版の購入に辿れました。

電子書籍(PDF版)購入の流れ

Spring Boot開発環境

書籍で解説されるコード断片を実行しようとする場合、DIコンテナを含めて各フレームワーク・ライブラリが動く環境が必要です。簡単にSpringアプリケーションを動かすのがSpring Bootの機能なので、まずはSpring BootでSpringアプリケーションを作って動かせるようにします。

書籍では、A.付録において、Spring Frameworkを使うアプリケーションの開発環境として、EclipseベースのSTS(Spring Tool Suite)の準備が説明されています。

Spring Bootの開発では、WebサービスのSpring Initializr へアクセスし、設定を入力するとプロジェクトの雛形がダウンロードできるので、これを展開してIDEに読ませるという流れになります。設定ではビルドツールにMavenかGradleを選択できます。

IntelliJ IDEAの場合、有償のUltimate版なら Spring Initializrにアクセスしなくてもプロジェクトの作成からSpring Bootを選択して雛形を生成できます。と言っても、プロジェクトの作成では裏でSpring Initializrをアクセスしているようです。無償のCommunity版の場合は、Spring Initializrにアクセスして生成した雛形を読み込んで使用する方法となります。

Spring Bootを使ってビルドしたアプリケーション(JAR形式)は、Webサーバーが組み込まれるので、JARファイルを実行するだけでWebサーバーを用意しなくてもWebブラウザからアクセスが可能になります。デフォルトではembedded TomcatらしきクラスファイルがJARに含まれていました。

IntelliJ IDEA(Ultimate版)でSpring Bootプロジェクト生成

新規プロジェクトを生成します(File > New > Project)

プロジェクト名、言語、ビルドツール、パッケージ名などを選択・入力します。

Dependencies で、Spring Webを選択します。これを選択することで、SpringのWeb機能の基本的な振る舞いを利用できるようになります。ブラウザにHelloメッセージを表示する最初のプログラムはこのライブラリを使用します。

アプリケーションのmainクラス(エントリーポイント)とテストクラスの雛形が生成されました。 生成されたmainクラスを実行すると、Webサーバー機能が動作し、デフォルトではデフォルトでは 8080ポートで待ち受けます。Webブラウザからlocalhost:8080に接続すると次のエラーが発生します。これは、ルート/にマッピングされる処理が未定義のため 404エラーとなっている場合のメッセージです。

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Oct 28 00:00:55 JST 2024
There was an unexpected error (type=Not Found, status=404).

Hello World! とメッセージを表示するWebアプリケーションとするために、生成されたmainクラスに@RestControllerアノテーションを付与し、HTTPのGETメソッドでルート/を指定された際にメッセージを返却するメソッドを@GetMappingアノテーションを付与して定義します。

package com.torutk.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    @GetMapping("/")
    String hello() {
        return "Hello World!";
    }

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

}

Hello World的なWebアプリとして単一クラスで完結させるのに、REST APIを扱う@RestControllerを使用しました。 コントローラのメソッドにREST APIのメソッド(GET)をマッピングし、メソッドの戻り値をAPIの戻り値にラップします。戻り値の型をStringとしているので単純な文字列が返却されますが、通常はリソースクラスを定義して返却します。

通常のWebアプリケーション用の@Controllerは、別途Viewに相当する仕組みを用意するか、@ResponseBodyを使うなどひと手間かかります。

史跡巡り 桶狭間合戦場・大高城・鷲津砦・丸根砦

先日、遅めの夏休みを取って、名古屋方面に一人旅をしてきました。 織田信長の足跡をたどって、初日は桶狭間合戦場と、大高城、鷲津砦、丸根砦の跡を巡りました。

地図に、大高城と鷲津砦、丸根砦、そして桶狭間合戦場と伝えられる場所をマークしました。また当時は大高城付近まで海となっており、地図で平地(緑色)の大半は海と考えられます。 出展:国土地理院ウェブサイト デジタル標高地形図 濃尾平野周辺をもとに、城・砦跡、合戦場をマークして記載

大高城は織田方から今川方のものになっており、鷲津・丸根の砦は大高城に対抗するため織田方が築いた砦です。桶狭間合戦の日に鷲津砦・丸根砦は今川方の攻撃を受け落城、今川義元は本隊を率いて沓掛城から大高城へ入る途中、桶狭間付近で織田信長の奇襲を受けて受けて打ち取られ、今川方は撤退していきました。

桶狭間合戦場の場所は、実際には特定されておらず、上述地図に記載した場所のほかに、旧東海道沿いの場所があるようです。

桶狭間合戦場公園

  • 小さな公園となっていて、織田信長今川義元の像などがあります。最寄り駅は名鉄有松駅で、徒歩25分またはバス10分ほどになります。

鷲津砦

  • JR東海道線 大高駅のすぐ北側にあります。小山の途中が跡地となっていますが、特に遺構などはないようです。

丸根砦

  • 鷲津砦跡から徒歩10分弱、尾根伝いに歩くと至ります。

大高城

  • 鷲津砦跡から徒歩20分、砦と違ってそこそこの大きさです。
経路

今回は、名古屋駅から名鉄で有松駅に向かい、有松駅からバスで桶狭間寺前で下車、NPO法人が運営する桶狭間古戦場観光案内所に寄ってから桶狭間公園とそこから数十mほどにある今川義元本陣跡(住宅の前に看板だけある)、そこから有松駅方面に徒歩で向かい、旧東海道沿いの有松伝統的建造物群保存地区を見ながら有松駅に着きました。

有松伝統的建造物群保存地区

名鉄有松駅からバスでJR大高駅に向かい、大高駅のすぐそばにある鷲津砦跡から丸根砦跡を回り、そこから大高城へ歩いて向かいました。

Webまわりの技術知識の獲得のための書籍購入

Java読書会BOFで「基礎からのサーブレット/JSP 第5版」を読みすすめていると、Webアプリケーションの技術知識として増やしたいことがいくつかでてきました。

  • Webページの見栄えをよくするには、HTMLに加えてCSSCascading Style Sheets)が必要
  • Webページに動きを伴わせるには、JavaScriptが必要か、Javaだけでもできるかを知りたい
  • HTTP/2やHTTP/3について知識をえておきたい

そこで、2、3冊書籍を買って読み始めました。

まずはHTMLとCSSの本から選んだのが「1冊ですべて身につくHTML&CSSとWebデザイン入門講座」(SBクリエイティブ刊 2024年3月)

JavaScriptをブラウザ上でどのように使うのか、フロントエンド全般に書かれた「フロントエンドの知識地図 ~ 一冊でHTML/CSS/JavaScriptの開発技術が学べる本」(技術評論社刊 2023年11月)

HTTP全般について、HTTP/2、HTTP/3も解説のある「Real World HTTP 第3版」(オライリージャパン刊、2024年4月)

H2 databaseでのレコード登録時間

Java読書会の宿題より

Java読書会「基礎からのServlet/JSP 第5版」を読む会(第3回)の宿題で、int(32bit整数)を超える行のテーブルを作成して、一気にintを超える行の更新をした時の戻り値を調べてみよう、の取り組みです。

H2データベースへのデータ登録に要する時間

primary keyもunique属性もない単一カラムのテーブルを定義

CREATE TABLE bigdata (
    id int
);
単純 insert

32bit符号付整数値の最大値がおよそ21億なので、30億行のレコードを登録します。 まずは、単純に INSERT INTO bigdata(id) values (1); のようにデータをインサートするクエリを30億回実行します。

  • 方法1 自動コミットが有効(デフォルト)
  • 方法2 自動コミットを無効にし、100万行ごとにcommit

方法1は、100万行のinsertに20秒を要しました。30億行のinsertに推定17時間要する見積です。自動コミットが有効だと、insertを実行するごとにコミットが働くので遅いだろうと想定していました。

方法2で、自動コミットを無効として、一定量毎にcommitをしてみました。 ところが、方法2でも、100万行のinsertに20秒を要し、方法1とほとんど処理時間が変わりません。

バッチinsert

次は、バッチ登録を実施します。複数のinsert文をバッチ化してからバッチを実行します。100から1000個のisertをバッチにまとめます。

     PreparedStatement ps = conn.prepareStatement("insert into bigdata values(?)");
       :       
    for (int i = 0; i < 1000; i++) {
        ps.setInt(1, count);
            ps.addBatch();
    }
    ps.executeBatch();

バッチ登録の場合、自動コミットはオフとし、最後にcommitします。*1

さて、期待していた登録速度ですが、なんと方法3でも100万行のinsertに20秒を要し、方法1、2とほとんど同じでした。

ここまでの結果から、H2 databaseはSQLのリクエストのオーバーヘッド、自動コミットのオーバーヘッドがほとんどなく、クエリの処理数がほぼそのまま処理時間になっているものと推測します。

すると、クエリの処理数を減らして登録行数を増やすことができれば処理時間を短縮できるのではと考えます。

複数行を1つのinsertで

ここで、insert文は、複数行のデータを登録することができます。

insert into bigdata(id) values (1), (3), (5), (7), (11), (13);

このinsert文は、6行のレコードを登録します。

  • 方法4 自動コミットを無効にして、10行のデータを1つのinsert文で登録する

100万行の登録が2-3秒と短縮することができました。

  • 方法5 自動コミットを無効にして、100行のデータを1つのinsert文で登録する

100万行の登録が1秒とさらに短縮することができました。これであれば、30億行の登録に50分の見積もりです。

実行環境
実行結果

*1:30億行となると、さすがに小分けにcommitした方が良いかも