複数バージョンのデータベースを同一のJavaアプリケーションで利用するには?

f:id:ts0818:20200517174110j:plain

レコード (record, vinyl record, 英語版ではgramophone record)とは、音声記録を意味し、主に樹脂などでできた円盤(最初期には円筒状の蝋管レコードを含む)に音楽や音声などの音響情報を刻み込み記録したメディアの一種を示すことが多い。音盤などその他の呼び方で呼ばれることもある。

レコード - Wikipedia

⇧ ただの「レコード」だと、何の記録か分からんのでは、って思ってきたけど、英語だと「gramophone record」って言うんですね。

音の再生の方法は信号としての振幅の情報の読み取り方と情報の増幅により異なる。針で読み取った振幅の情報を、機械的に増幅する蓄音機の時代、電気信号に変えて増幅するレコードプレーヤーの時代に大まかに分類することができる。

レコード - Wikipedia

⇧ 「レコード」はよく、音質が良いって言いますよね。

www.denon.jp

最近はCDを超えたデータ量を持つハイレゾ音源も一般的になってきましたが、「ハイレゾ」が目指しているのは、レコードの音質だと聞いたことがあります。

田林:それは事実です。CDには20kHzという高音まで収録されていますが、規格上20kHzを超える高音はスパっと切られてしまっています。その点ハイレゾ音源は40kHz以上の音が入っているのでレコードに近い感触があると言われています。

レコードエンジニアが語るアナログの魅力 | デザインシリーズ | Denon公式

⇧ 「ハイレゾ」の音質が目指してるものが「レコード」の音質なんですね。

情報が整理されてて、難しい話ですけどなんとなくイメージができますね。

 

分かりやすい説明と言えば、  

industry-co-creation.com

当日は、本当に60歳〜80歳くらいの方々が来られていました。

結局、その時に選んだ話のネタは「お孫さんと一緒に電器屋に行ってドヤ顔ができるITの用語集」というものでした。

皆さんメモを取りながら聴いて下さり、ものすごく喜んで頂けました。

電器屋さんに行って説明されて全然分からなくて腹が立っていたのが、随分分かるようになった。これでドヤ顔ができる」と言って頂きました。

その方達は「成長しよう」という意欲を持っていたんです。

「新しい知識を得ることで、ハッピーになる」澤円さんが語る人生100年時代の成長とは | 【ICC】INDUSTRY CO-CREATION

⇧ 澤さん。

やっぱり分かりやすく説明できる方ってのは偉大ですね。そのために、費やしている時間は膨大なものになるでしょうね、努力の結果なんだな~。

ちなみに、澤さんは「ビル・ゲイツが卓越した社員にのみ授与する「Chairman’s Award」を受賞した唯一の日本人」らしいですね、「プレゼンテーションの神」と呼ばれるのも納得。

 

はい、脱線しました。

 

アプリケーションの継ぎ足しなんかを行っていくと、データベースのバージョンが混在したまま運用しなければいけないこと、あるあるだよね?

え?無いですか?

そんなこんなで、同一のアプリケーションで複数バージョンのデータベースを利用する方法が存在するのか調査してみました。

レッツトライ~。

 

クラスローダーを分ける必要があるらしい

読み込むjdbc毎に、クラスローダーを分けて上げる必要があるんですと。

stackoverflow.com

If you don't register the drivers you avoid them being loaded by the same classloader.

Then you can create connections using the two different drivers by loading them through separate classloaders:

Using Multiple Oracle JDBC drivers in one Java application? - Stack Overflow

⇧ 同一のクラスローダーで読みこむことを避けろ、と仰っていますね。

 

何故、クラスローダーを分ける必要があるのか?

そもそも、Javaのアプリケーションというものは、どう動いているかと言うと、

Java仮想マシン (Java virtual machineJava VMJVM) は、Javaバイトコードとして定義された命令セットを実行するスタック型の仮想マシン

Java仮想マシン - Wikipedia⇧  JVMがあって、

APIやいくつかのツールとセットでJava実行環境 (JRE) としてリリースされている。この環境を移植することで、さまざまな環境でJavaプログラムを実行することができる。

Java仮想マシン - Wikipedia

⇧  JVMで動くことを前提にしてます、何故なら、環境に依存せずに動くようにしたいという狙いがあるわけね。

⇧ ってのを目指しているわけでしたと。実際はその通りには実現できてないんですけどね、あくまで理想ね。

 

実行の仕組みとしては、

ソースコードは一旦Javaバイトコードへとコンパイルされ、さらにインタプリタまたはJITコンパイラによりネイティブコードに変換されて実行される。Java APIJVMの両者でJava実行環境 (JRE) を構成する。

Java仮想マシン - Wikipedia

⇧ ってな具合なんですが、

この、Javaバイトコード(「中間言語」とかとも呼ばれたり、一貫してないですが)って言うのが、クラスなんですと。

 

んで、クラスを読み込むのが、

JavaクラスローダーJava class loader)とは、Java仮想マシン (Java VM; JVM) の一部で、JavaクラスJava仮想マシンに動的にロードする役割を持つ。通常、クラスは必要になったとき初めてロードされる。

Javaクラスローダー - Wikipedia

⇧ クラスローダーなんですと。

ソフトウェアのライブラリとは、オブジェクトコードと多かれ少なかれ関連しているが、Java言語ではライブラリはJARファイルに格納され、様々なオブジェクトを格納することができる。

Javaクラスローダー - Wikipedia

⇧ でJARファイルは、Javaのライブラリの集まりであって、つまり、

クラスはコードに名前をつけた一つの単位であり、クラスローダーはライブラリを見つけて内容をロードし、ライブラリに含まれるクラスをロードする責務を持つ。

Javaクラスローダー - Wikipedia

⇧ クラスの集まりであるから、クラスローダーはJARを読み込むんだと。

クラスのロードは「必要に応じて」であり、すなわちクラスがプログラムに実際に必要になるまで行われない。

Javaクラスローダー - Wikipedia

⇧ クラスはプログラミングの処理で必要になったタイミングで読み込まれるんだけど、

指定された名称のクラスは、あるクラスローダーにたった一度だけしかロードされない。

Javaクラスローダー - Wikipedia

⇧ クラスローダーで読み込まれるクラスは一度しか読み込まれないんだそうな。

 

Oracleさんの見解は、

www.oracle.com

いったんJVMにクラスがロードされたら、同じクラス(繰り返しますが、同じクラスです)が再びロードされることはありません。

クラス・ローダーAPIの修正によるデッドロックの回避

⇧ やはり、一度だけしか読み込まれませんと。

ここで疑問になるのは、"同じクラス"とはいったい何を意味するのか、という点です。 オブジェクトが特定の状態やIDを保持し、オブジェクトは常にそのコード(クラス)に関連付けられているいう条件と同様に、JVMにロードされたクラスには特定のIDが指定されています。

クラス・ローダーAPIの修正によるデッドロックの回避

⇧ で、このIDってのが何なのかの説明が無いのでいまいち分からんけども。

Javaでは、クラスはその完全修飾クラス名によって識別されます。 完全修飾されたクラス名は、パッケージ名とクラス名で構成されています。 

クラス・ローダーAPIの修正によるデッドロックの回避

⇧ パッケージ名が大事よね。

しかし、JVMでは、完全修飾クラス名に加えて、クラスをロードしたClassLoaderのインスタンスによってクラスが一意に識別されます。

クラス・ローダーAPIの修正によるデッドロックの回避

⇧ クラス・ローダーが複数ある場合を考慮した仕様ですかね。

このように、パッケージPgに含まれるクラスClが、クラス・ローダーKlassLoaderのインスタンスkl1によってロードされた場合、C1のクラス・インスタンスC1.class)のJVMでのキーは(Cl, Pg, kl1)になります。 つまり、(Cl, Pg, kl1)と(Cl, Pg, kl2)という2つのクラス・ローダー・インスタンスは同一ではなく、これらによってロードされたクラスもまったく異なるものであり、互いの型に互換性はありません。

クラス・ローダーAPIの修正によるデッドロックの回避

⇧ つまり、クラス・ローダーが異なれば、ロードされるクラスは互いに別物ですと。

んで、クラス・ローダーには、

  • bootstrap class loader
    • 中核のJavaライブラリをロードする(<JAVA_HOME>/lib ディレクトリ)。ネイティブコードで記述されている。
  • extension class loader
    • 拡張ディレクトリ (<JAVA_HOME>/lib/ext や、java.ext.dirsプロパティで指定された他のディレクトリ) にあるコードをロードする。これは、sun.misc.Launcher$ExtClassLoader クラスで実装されている。
  • system class loader
    • java.class.path、すなわちシステム環境変数 CLASSPATH にあるクラスをロードする。こちらは、sun.misc.Launcher$AppClassLoader クラスで実装されている。

 の3つが存在するんですと。 

⇧ で、このうち、「system class loader」つまり「sun.misc.Launcher$AppClassLoader」が、「java.class.path」にあるクラスを読み込む。

クラス・ローダーが、一度しか読み込まれない仕組み上、ClassLoaderを継承した独自のクラス・ローダーのクラスを作成することで、「sun.misc.Launcher$AppClassLoader」が、それらのクラスを読み込んでくれるということかと。

 

ただ、クラス・ローダーは、

blog.dreamarts.co.jp

Connector/J 5.1.41 において、Tomcat で webapp の再読込などが起きたときに、この AbandonedConnectionCleanupThread が止まらずリークが起きるというバグが修正された。(https://bugs.mysql.com/bug.php?id=69526

MySQLのJDBCドライバでメモリリークがあった話 | DA BLOG

それまでは各 webapp で起動していたこのスレッド。修正後はシングルトン化され、同時に Tomcat で webapp の交代が発生した場合など、クラスローダーが変わると AbandonedConnectionCleanupThread が自動的に終了するようになった。
https://github.com/mysql/mysql-connector-j/commit/cd38fe0fdeba0cd105bd7bf7bfbd8a30a59dff5d

MySQLのJDBCドライバでメモリリークがあった話 | DA BLOG

⇧ 結構、悩みの種になる存在ではあるらしい。

って言うか、クラス・ローダーに依存する設計のJDBCドライバってどうなの?...JDBCドライバって全般的にそんな感じなのかね?

qiita.com

ApacheTomcatのJVM Optionに java.endorsed.dirs が含まれているとJDK 9以降ではJavaの起動に失敗します。

OpenJDK 11へ変更後にApacheTomcat 8が起動しなくなる件の解決方法 - Qiita

Java 11 から「endorsed」ディレクトリはサポートされなくなったらしい...無くすなら何で「endorsed」ディレクトリを作ったんですかね...

まぁ、何が言いたいかと言うと、単純にクラス・ローダーを別けるって言うのはリスクもありそうってことですかね...

 

Javaって、

The Java™ programming language is a general-purpose, concurrent, class-based,
object-oriented language. It is designed to be simple enough that many programmers can achieve fluency in the language.

 https://docs.oracle.com/javase/specs/jls/se6/jls3.pdf 

⇧ 単純に設計するって思想だったはずでは...

  

で、左様に厄介極まりないクラス・ローダーは、JVMの一部らしく、 

medium.com

⇧ 上記サイト様のようなイメージ図が、ネット上の大半を占めるので、これがJavaが実行されるフローのイメージに一番近いってことですかね。

んで、Java 11 からは、javacでのコンパイル不要らしいから、「Source Code」から「Byte Code」にどうやって変換してるのよ?ってモヤモヤが残るというね...

 

脱線しましたが、『何で異なるバージョンのJDBCドライバを使用するためにクラスローダーを別ける必要があるか』は、おそらく、『指定された名称のクラスは、あるクラスローダーにたった一度だけしかロードされない。』っていう制約があるためかと。

  

違うバージョンのデータベースを使わざるえない状況って、現場であるあるな気はするんだけどな~、何でこんなに情報が無いんだろう?

 

java.sql.DriverManager と javax.sql.DataSource 以外に何かあるんだっけ?

で、Javaでデータベースを使う時に必要になってくるのがJDBCドライバだと思うんだけど、こいつを読み込むのには、「java.sql.DriveManager」「javax.sql.DataSource」のどちらかが使われるらしい。

itdoc.hitachi.co.jp

WebアプリケーションからJDBCを使用してデータベースに接続をする場合,サーブレットエンジンモードとJ2EEサーバモードではWebアプリケーションの実装方法および設定方法が異なります。

データベースとの接続

⇧ という違いがあるようです。

サーブレットエンジン」使ってると、

サーブレットエンジンモードのWebアプリケーションの場合,使用できるのはjava.sqlパッケージで定義されたクラス,インタフェースだけです。java.sql.DriverManagerクラスのgetConnectionメソッドを使ってデータベースコネクションを取得し,コネクションプール機能が使用できます。

データベースとの接続

⇧ つまり、「java.sql.DriveManager」しか利用できない。

J2EEサーバ」使ってると、

J2EEサーバモードのWebアプリケーションの場合,java.sqlパッケージとjavax.sqlパッケージで定義されたクラス,インタフェースが使用できます。このうち,javax.sql.DataSourceクラスのgetConnectionメソッドを使ってデータベースコネクションを取得する方法をお勧めします。

データベースとの接続

⇧ つまり、「java.sql.DriveManager」「javax.sql.DataSource」どちらもイケますと。

ただ、「J2EE」というのは、

⇧ だいぶ古いらしいので、状況が変わってるのかしら?

いまは、Java EE 8 とか出てるみたいだし。

www.publickey1.jp

JDK に至っては、 

jdk.java.net

⇧ バージョン15まで出たらしいし... 

まぁ、何はともあれ、データベースの接続には、「java.sql.DriveManager」か「javax.sql.DataSource」しか選択肢が無いという前提で話を進めるとします。(フレームワークとか使ってると、このへん隠蔽されてしまっている気がするけど)

java.sql.DriveManager」を利用する場合は、

getConnectionメソッドが呼び出されると、DriverManagerは、初期化時にロードされたドライバや、現アプレットあるいはアプリケーションと同じクラス・ローダーを使用して明示的にロードされたドライバの中から適切なドライバを探そうとします。

DriverManager (Java Platform SE 8)

⇧ 一番初めに読み込まれたjdbcドライバだけ使われるってことかね?

つまり、「java.sql.DriveManager.getConnection」が2回呼ばれたとしても、1回目の設定が使われ続けるわけですと。

それを回避するには、クラスローダーを別々にする必要があるんですと。

 

「javax.sql.DataSource」を利用する場合は、

DataSourceオブジェクトを介してアクセスされたドライバは、自分自身をDriverManagerで登録しません。むしろ、DataSourceオブジェクトがルックアップ操作により取得されて、Connectionオブジェクトを作成するために使用されます。基本実装では、DataSourceオブジェクトにより取得された接続は、DriverManager機能により取得した接続と同じものになります。

DriverManager (Java Platform SE 8)

⇧ ってあるけど、DataSourceはインターフェイスなので、DataSourceをimplementsしたオブジェクトを複数作成すれば、理論上は、バージョンの異なるjdbcの利用が可能になりそうな気はする。

実際、

access.redhat.com

JBossでできるよって言ってますね。

 

まとめると、複数のバージョンのjdbcドライバを利用する場合、

  • java.sql.DriveManager
    • 複数のクラスローダーを用意
  • javax.sql.DataSource
    • 単一のクラスローダーでOK

ってことになるかと。

 

Spring Bootで試してみようと思ったけど...

ここで、暗礁に乗り上げる...

何故かというと、Spring Bootは「複数のバージョン」のjdbc の利用に対応していないっぽいんじゃないかと。

少なくとも、Maven とか使ってる場合は、1つのpom.xmlには、1つのバージョンのjdbcドライバしか設定できない気がするしね。

おそらく、Gradleでも同じだとは思うけど。

やるとしたら、「Maven」や「Gradle」のようなビルドツールとは別に、独自でjdbcのドライバを追加すればできるかもしれない。

って思ってたら、

stackoverflow.com

No. Maven will only resolve one dependency in your module and will omit the other versions to avoid any conflict. Even if multiple versions of the same dependency are used in the whole dependency hierarchy, Maven will pick one version using the "nearest in the dependency tree" strategy.

Multiple versions of the same dependency in Maven - Stack Overflow

⇧ 確かに、普通に使うと1つのバージョンのみしか認識してくれんのだけれど、

It is possible to specify different dependency versions using different profiles. For each version of Bukkit a profile can be defined and activated. Still if you activate more than one profile, only one version would be used.

Multiple versions of the same dependency in Maven - Stack Overflow

⇧ 異なる「profiles」を使えば解決できるかも、とあるけど、そもそも「profiles」って何なんすかね?

dzone.com

Every enterprise application has many environments, like:

Dev | Test | Stage | Prod | UAT / Pre-Prod

Each environment requires a setting that is specific to them. For example, in DEV, we do not need to constantly check database consistency. Whereas in TEST and STAGE, we need to. These environments host specific configurations called Profiles.

How to Use Profiles in Spring Boot - DZone Java

⇧ アプリケーションの環境に関する設定を「profiles」と言うそうな。

This is simple — properties files!
We make properties files for each environment and set the profile in the application accordingly, so it will pick the respective properties file. Don't worry, we will see how to set it up.

How to Use Profiles in Spring Boot - DZone Java

⇧ で「profiles」の手っ取り早い実現方法は、「properties files」って言ってますね。

要するに、

  • application.properties
  • application.yml

なんかのようなファイルのことを「profiles」と捉えてるみたいですね。

 

ただし、「application.properties」や「application.yml」なんかではjdbcのバージョンを判別できないから、どちらにしろ、1つの pom.xmlMavenを使ってる場合) では実現は厳しそうな気がするんだけど...。

 

もう、あれかな?

マイクロサービスとかが流行ってるから、マルチモジュールにしろってことなのかな?

www.quora.com

But I would suggest you to just change the version of your Spring JDBC dependency and then compile and run the project before going ahead with any other changes .

Also , one more point to be mentioned is that the Spring version 2x is not compatible with Java versions above 1.6 . So if that is prompting you to maintain two different versions of Spring JDBC , then you should think about separating the module into a separate project . Having two different projects with their own executables (jar , ear , war , etc) is achievable but is cumbersome to maintain . Rather you can go for all an overall upgrade of Java and Spring versions for your entire project .

https://www.quora.com/How-do-I-use-two-different-versions-of-the-same-artifact-Spring-JDBC-in-a-project-One-used-JDBC-4-3-codes-in-the-existing-project-code-itself-and-another-to-support-existing-code-in-another-dependency-that-uses-JDBC

⇧ モジュール分けた方が良いって言ってますしね。

「マルチモジュール」については、

Mavenの場合は、

maven.apache.org

As seen in the introduction to the POM, Maven supports project aggregation in addition to project inheritance. This section outlines how Maven processes projects with multiple modules, and how you can work with them more effectively.

Maven – Guide to Working with Multiple Modules

⇧ 「Multiple Modules」って仕組みで可能ですと。

Gradleの場合は、

stackoverflow.com

⇧ 「Multiple Modules」って概念がそもそも無くて「multi project」ってことになるらしい。言葉の表記ゆれなのか、そもそも意味合いがまったく異なるのか...この結局どうとらえて良いか解釈に困るような記載は本当にどうにかならんかね...

 

Spring frameworkJavaフレームワークのスタンダードっぽいように聞いてきたけど、どうも柔軟性には欠ける気が...

まぁ、フレームワーク全般がそんな気がするけどね...

できないことはできないってドキュメントに書いといてくれれば、助かるんですけどね。 

 

Mavenの「Multiple Modules」で試してみる

で、「Multiple Modules」ってどうやって作れば良いのか?

www.techscore.com

fa11enprince.hatenablog.com

⇧ 上記サイト様によりますと、「Multiple Modules」っていうのは、「親」と「子」の入れ子構造になるらしい。

 

Eclipseで試してみますか。

f:id:ts0818:20200506111237p:plain

今回は「Maven プロジェクト」で。(「Spring Boot」でもビルドツールで「Maven」を指定すればできるのかも知らんけど。)

f:id:ts0818:20200506111148p:plain

f:id:ts0818:20200506111646p:plain

アーティファクトId」はどれでも良いらしい。

f:id:ts0818:20200506111829p:plain

「グループ Id」「アーティファクト Id」を適当に入力して、「完了(F)」で。

f:id:ts0818:20200506112114p:plain

Mavenプロジェクト」が作成されるので、「pom.xml」を開きます。

f:id:ts0818:20200506112507p:plain

「Spring Boot」が利用できるように「pom.xml」を修正します。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
  </parent>

  <groupId>com.example.multiple-module</groupId>
  <artifactId>multiple-module-project</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>pom</packaging>

  <name>multiple-module-project</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>11</java.version>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

JUnit 5系を使えるようにしたので、テストが自動で作成されエラーが出るので、修正。(お試し的な感じなんで、テストは無くても良いかも。)

package com.example.multiple_module.multiple_module_project;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

/**
 * Unit test for simple App.
 */
public class AppTest
{
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public AppTest( String testName )
    {
        super( );
    }

    /**
     * Rigourous Test :-)
     */
    @Test
    public void testApp()
    {
        assertTrue( true );
    }
}

そしたら、「子」を作成していきます。

f:id:ts0818:20200506115829p:plain

Maven モジュール」を選択で「次へ(N)>」をクリック 。

f:id:ts0818:20200506115938p:plain

適当に「モジュール名(M):」を入力し、「親プロジェクト:」のとこで「参照(O)...」をクリック。

f:id:ts0818:20200506120144p:plain

f:id:ts0818:20200506120214p:plain

「次へ(N)」。

f:id:ts0818:20200506120243p:plain

ここは、何でも良いらしい。「次へ(N)」。

f:id:ts0818:20200506120350p:plain

パッケージ名に数字は使えないらしいので、修正。

あと、パッケージ名が「完了(F)」で。

f:id:ts0818:20200509180308p:plain

そしたら、何故か「親」の「pom.xml」でエラー。「<module>」タグに値が設定されてない...Eclipseのバグですかね?

f:id:ts0818:20200506121151p:plain

f:id:ts0818:20200506121128p:plain

「子」の「プロジェクト名」を設定して上げれば良いらしい。

f:id:ts0818:20200506121355p:plain

「子」の「pom.xml」で警告が出てるので、編集。

同じ様に、もう2つ「Maven モジュール」を作成すると、以下のような構成になります。

f:id:ts0818:20200517172128p:plain

上記の構成について、pom.xml について焦点を当ててみると、pom.xml は、

  • multiple-module-project
    • pom.xml
      • app
      • multiple-versions-db-1
      • multiple-versions-db-2

⇧ 全部で、4つできてる感じですかね。

 

そして、終盤になって情報が見つかるパターン...

spring.pleiades.io

 

で、Mavenの「Multiple Modules」を作成したばかりの状態だと、ただ、「main」メソッドがそれぞれにあるだけの普通のクラスになってるみたいです。

なので、それぞれの「main」メソッドを持ってるクラスを修正(System.out.printlnで出力される文字を変えただけです。)して実行してみます。

f:id:ts0818:20200506162523p:plain

f:id:ts0818:20200506162159p:plain

 おはよう!

f:id:ts0818:20200506162614p:plain

f:id:ts0818:20200506162355p:plain

こんにちは! 

f:id:ts0818:20200506162703p:plain

f:id:ts0818:20200506162733p:plain

こんばんは!

ってな感じで文字列が出力されました。それぞれ、独立してる感じです。

 

www.baeldung.com

Now we can build all three modules at once. In the parent's project directory, run:

mvn package

This will build all the modules, we should see the following output of the command:

Maven – Guide to Working with Multiple Modules

⇧ 上記サイト様によりますと、「親」プロジェクトで、mvnコマンド実行すれば、「子」プロジェクトも実行されるそうな。

f:id:ts0818:20200506164610p:plain

f:id:ts0818:20200506164710p:plain

 

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] multiple-module-project                                            [pom]
[INFO] Archetype - multiple-module-versions-db-1                          [pom]
[INFO] Archetype - multiple-module-versions-db-2                          [pom]
[INFO] 
[INFO] --------< com.example.multiple-module:multiple-module-project >---------
[INFO] Building multiple-module-project 0.0.1-SNAPSHOT                    [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] ---< com.example.multiple-module-db-1:multiple-module-versions-db-1 >---
[INFO] Building Archetype - multiple-module-versions-db-1 0.0.1-SNAPSHOT  [2/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] ---< com.example.multiple-module-db-2:multiple-module-versions-db-2 >---
[INFO] Building Archetype - multiple-module-versions-db-2 0.0.1-SNAPSHOT  [3/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for multiple-module-project 0.0.1-SNAPSHOT:
[INFO] 
[INFO] multiple-module-project ............................ SUCCESS [  0.005 s]
[INFO] Archetype - multiple-module-versions-db-1 .......... SUCCESS [  0.003 s]
[INFO] Archetype - multiple-module-versions-db-2 .......... SUCCESS [  0.001 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.610 s
[INFO] Finished at: 2020-05-06T16:47:25+09:00
[INFO] ------------------------------------------------------------------------

そうすると、「target」ディレクトリに「jar」もしくは「war」ファイルができるらしいんだけど、できてない...

どうやら、

howtodoinjava.com

⇧ 「子」の「pom.xml」の「package」タグの値を「war」「jar」にする必要があったらしい。

ちなみに、「war」は、「web.xml」とか無いと駄目らしい、っていうか失敗した。

www.codeflow.site

⇧  上記サイト様によりますと、

  • WEB-INF/web.xmlを用意
  • pom.xmlで「maven-war-plugin」の「failOnMissingWebXml」オプションで「false」を設定

 のどっちかで対処しない限り「war」の作成はできないようです。

今回は、「jar」にしました。

f:id:ts0818:20200506175114p:plain

⇧ できました。

ただ、やりたいことは、これじゃないのよね。

Springのマルチプロジェクトの説明を見た感じ、エンドポイントは1つにするらしい、つまり、「main」メソッドは1つに削るってことになるかと。

 

ちなみに、  

blogs.oracle.com

Oracle has published its Oracle Database JDBC client libraries on Maven Central. From now on you can find Oracle Database related jar files under the com.oracle.database group id. You will find all libraries from version 11.2.0.4 (e.g. ojdbc6) to 19.3.0 (e.g. ojdbc10).

Oracle Database client libraries for Java now on Maven Central | Oracle Developers Blog

Oracle さん、Maven Central リポジトリJDBCドライバを公開してくれたそうです!

Oracle Database 11g 以降のドライバに対応してくださってます。

Going forward, Oracle will use Maven Central as one of the primary distribution mechanisms for Oracle Database Java client libraries, meaning that you will also be able to find new versions of these libraries on Maven Central in the future.

Oracle Database client libraries for Java now on Maven Central | Oracle Developers Blog

⇧ なので、これからは、JDBCドライバをダウンロードしておかなくても、MavenやGradleといったビルドツール経由でダウンロードできるようです。

 

はい、脱線しました。

よく、Javaフレームワークの紹介で「Spring framework」って言葉が出てくるんだけど、この紹介の仕方が良くない気がするんですよね。

spring.io

From configuration to security, web apps to big data—whatever the infrastructure needs of your application may be, there is a Spring Project to help you build it. Start small and use just what you need—Spring is modular by design.

https://spring.io/projects/

⇧ にあるように、「プロジェクト」の1つとして「Spring framework」や「Spring Boot」がある状態なんです。

しかも、最近は「Spring Boot」推しが目立って来ていて、そうなった場合、最早、『Javaフレームワークは何を使ってる?』という問いに対して「Spring framework」を使ってますとは言えないんじゃないか、と思ったんですよ。

 

codezine.jp

 Spring Bootはこういった状況の中で、組み合わせの成功パターンを管理するためのフレームワークと言えます。従って、Spring Bootだけを見ていると全体像がつかみにくいのですが、Spring Frameworkの特徴やこれまでの課題点が分かると、Spring Bootがより理解しやすくなると思います。

多様化するJavaのフレームワーク問題を解決する、「Spring Boot」とは? (3/3):CodeZine(コードジン)

⇧ 上記サイト様は「Spring Boot」をフレームワークと捉えてますと。まぁ、そう言いたくなるよね、だって「Spring framework」って言葉どこにも出てこないし。

 

ただ、無茶苦茶ややこしいのが、「Spring Boot」を使うと「Spring framework」を構成してたライブラリ群も同梱されてくるというね。

f:id:ts0818:20200509132213p:plain

最早、「Spring framework」という言葉を使っている時に、どういったシチュエーションでのことを言ってるのか分からなくなるという事態になってる気がするんですよ。

 

ただ、「Spring Boot」を使った場合、「Spring framework」を構成してるものを全て含むのかというと、さにあらず。

docs.spring.io

f:id:ts0818:20200509131300p:plain

⇧ 「Spring framework」のドキュメントにあるように、「Data Access」とかは「Spring Boot」を使っても同梱されてきてないと思われる。まぁ、「Jackson」とかのライブラリを「Data Access」と捉えるんであれば、「Spring Boot」に同梱されてるとも言えますが。

それでも、「Maven 依存関係」を見た感じ、データベースへのアクセス系のライブラリ群は見当たらないんですよ。

そこで、「Spring Boot」の場合は、

spring.io

Spring Data’s mission is to provide a familiar and consistent, Spring-based programming model for data access while still retaining the special traits of the underlying data store.

https://spring.io/projects/spring-data

⇧ 「Spring Data」プロジェクトを使えということらしい。

んで、「Spring Data」の中で、データベースを扱うとなると、

  • Spring Data JDBC
  • Spring Data JDBC Extentions
  • Spring Data JPA
  • Spring Data MongoDB
  • Spring Data Redis
  • Spring Data RDBC2
  • Spring Data for Apache Cassandra

このあたりが、候補になってくるらしい。

今回は、データベースとして、Oracle Database  を使おうと思うので、「Spring Data JDBC」か「Spring Data JPA」のどっちかになるのかなと。

 

今回は、「Spring Data JDBC」を使っていこうかと。

https://docs.spring.io/spring-data/jdbc/docs/current/changelog.txt

⇧  履歴を見た感じ、「Spring Data JDBC」はまだプロジェクトとしては、かなり新しそうですね、2018-02-06 が開始っぽい?感じなので、まだ、2年ちょっと。

qiita.com

⇧ バグとかもあったりしたようです。

 

2020年5月20日(水)追記: ↓ ここから

悲報...

jira.spring.io

As mentioned in the SO answer, spring-data-jdbc currently doesn't support multiple datasources.
It would be great to add this support in future releases.

Created:
Updated:

https://jira.spring.io/projects/DATAJDBC/issues/DATAJDBC-321?filter=allopenissues

⇧ Oh, my gosh...

2019年1月21日から、Spring Data JDBC は multiple datasources をサポートしとらんのだって...

いつサポートされるようになるんかね...

2020年5月20日(水)追記: ↑ ここまで

 

で、何やかんやで、むちゃくちゃ泥沼にハマったんだけど、「Multiple Modules」ってのは初期状態だと、何か「親」「子」の入れ子関係になってるんたけど、ネットの情報を見ると、みんな完全に「親」は無視して「子」だけで構成してたと言うね...

spring.pleiades.io

⇧ まぁ、公式でも「親」は pom.xml 以外は完全に無視してますな、何か「Multiple Modules」って残念な仕様ですね。

しかも、pom.xml に他の module を依存関係として追加する必要がありますと、紛らわしいな。

試しに「親」のpom.xml に、他の module を依存関係として追加したらEclipseのビルドが永遠に終わらない事態になりました...

f:id:ts0818:20200510124151p:plain

f:id:ts0818:20200510124227p:plain

怖すぎるんですけど...

「親」が「子」を見つけられないらしいんだけど、何故なのかが全く分からず...

致し方ないので、「親」のpom.xml に追加した他の module の依存関係を削除する方向で。とりあえず、ビルドを中断するところから。

「タスクマネージャー」で強制終了して、Eclipse再起動して、「自動的にビルド(M)」のチェックが付いてる場合は、

f:id:ts0818:20200510122752p:plain

チェックを外すことで、ビルドが中断されるので、

f:id:ts0818:20200510122923p:plain

その隙に、「親」の pom.xml に追加した他の module の依存関係を削除することで落ち着きました。

結局、理由は分からんのだけれど、

www.techscore.com

⇧ 「親」のpom.xml に、他の module の依存関係を追加したら駄目ってことですかね。

 

そして、 「profiles」が非常に分かりづらい...

www.it-swarm.dev

stackoverflow.com

⇧ カオス過ぎる...

 

しかも、

spring.pleiades.io

実行時にライブラリを使用するアプリケーションと衝突する可能性があるため、application.properties をライブラリに配置すること はお勧めしません (クラスパスから読み込まれる application.properties は 1 つだけです)。

Spring Boot マルチモジュールプロジェクトの作成 - コードサンプル

⇧ 「application.properties」ってファイル名は、1つだけのほうが良いって言ってますね、ただ、じゃあどういう方法があるのかは説明してくれず...。

そして、

stackoverflow.com

Maybe another approach could be to define specific profiles for each module and use the application.properties file just to specify which profiles are active using the spring.profiles.include property.

domain-module
- application.properties
- application-domain.properties

app-module
- application.properties
- application-app.properties

and into the application.properties file of app-module

spring.profiles.include=domain,app

https://stackoverflow.com/questions/23138494/spring-boot-application-properties-maven-multi-module-projects

⇧ 平気で複数の「application.properties」を使ってる御仁もおられるのですが...全然統一されてないじゃないですか...情報が不透明過ぎる...

 

stackoverflow.com

Probably the issue is that the application.properties is coming from the web project rather than data. You can't inherit these and the application.properties files are not aggregated, it just comes from the last project in the chain. You could try renaming the properties file in the data project to e.g. data.properties and ensure it is loaded with @PropertySource(value = {"data.properties"}

java - using properties in a multi module Spring Boot project - Stack Overflow

⇧ 上記サイト様によりますと、やっぱり、「Multiple Modules」の場合は、「application.properties」は読み込まれても、上書きされちゃうから、ファイル名を変えた方が良いって言ってますね。

そして、どうやら、「application.properties」以外のファイル名にした場合、配置場所は「/src/main/resources」にしないと駄目っぽい気がする

 

そして、悲報は続く

qiita.com

モジュール毎にapplication.properties(yml)を作り、それぞれ独自の設定値を用意しても、優先されるapplication.properties(yml)は1つだけ。
他のモジュールの設定値を取得してくるようなこともしなさそう。
であれば、プロジェクトルートであったりメインのモジュールで管理したほうが良さそう。

SpringBootでモジュールを追加する - Qiita

⇧ 何か、モジュール毎のprofile を一度に使うのは無理みたい...

Spring Bootで複数バージョンのデータベースを同一のアプリケーションで利用するのは無理そうね...

はぁ...

壮大な時間の無駄でしたな(涙)

Spring は、実現できることと実現できないことが分かり辛いな...

異なるデータベースを利用する方法ってのは見つかるんだけどな~

って思ってたら、

qiita.com

⇧ 上記サイト様の方法と、「Multiple Modules」で各モジュールのpom.xml で別々のバージョンのJDBCドライバをダウンロードしてきておけば、「複数バージョンのデータベースの利用の実現」がイケそうですかね。

試してみて、実現できたら追記させていただくということで。

 

今回はこのへんで。