はじめに (対象読者・この記事でわかること)

この記事は、Java 9以降のモジュールシステム(JPMS)を導入したプロジェクトで、データベース関連の機能を利用する際に「パッケージ javax.sql は複数のモジュールからアクセス可能です: <unnamed>, java.sql」という警告やエラーに遭遇したことのあるJava開発者を対象としています。

この記事を読むことで、この警告が発生する背景にあるJavaモジュールシステムの仕組みと、javax.sqlパッケージの特殊な位置付けが理解できます。また、具体的にどのような原因が考えられ、どのようにしてこの問題を回避・解決できるのか、その具体的な手順と解決策を学ぶことができます。これにより、モジュールシステムがもたらす依存関係の競合を解消し、クリーンな開発環境を構築できるようになります。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 * Javaの基本的なプログラミング経験 * Javaモジュールシステム (JPMS) の基本的な概念(module-info.java、モジュールパス、クラスパス) * MavenまたはGradleなどのビルドツールを用いた依存関係管理の経験 * JDBC (Java Database Connectivity) の基本的な知識

Javaモジュールシステムとjavax.sqlパッケージの複雑性

Java 9で導入されたJava Platform Module System (JPMS) は、Javaアプリケーションの構造化、依存関係の明確化、そしてランタイムの最適化を目的としています。各モジュールは、どのパッケージを外部に公開(exports)し、どのモジュールに依存するか(requires)を明示的に宣言します。これにより、従来のクラスパスベースのシステムが抱えていた「巨大なJAR」問題や、意図しない依存関係の問題を解決しようとしました。

しかし、この新しいモジュールシステムは、特に移行期において、既存のライブラリやフレームワークとの間で互換性の課題を生み出すことがあります。その典型的な例の一つが、javax.sqlパッケージに関する「パッケージ javax.sql は複数のモジュールからアクセス可能です」という警告やエラーです。

javax.sqlパッケージは、JDBC(Java Database Connectivity)のコアAPIを拡張するもので、データソース(DataSource)やコネクションプーリングなど、高度なデータベース接続機能を提供します。このパッケージは、元々Java EE(現在のJakarta EE)の一部としても提供されてきましたが、Java SE 6以降、javax.sqlの大部分のクラスがJava SEの一部として標準化されました。

JPMSの観点から見ると、javax.sqlパッケージは、JDKの標準モジュールであるjava.sqlモジュールによってエクスポートされています。つまり、JDK自身がjavax.sqlを提供しているということです。ここで問題となるのが、アプリケーションがクラスパス(<unnamed>モジュール)上で、何らかの外部ライブラリ(例えば、古いJDBCドライバ、古いJava EE APIのJAR、あるいは特定のコネクションプーリングライブラリなど)が独自にjavax.sqlパッケージを提供している場合です。

この状況が発生すると、Javaモジュールシステムは、同じjavax.sqlパッケージがJDKのjava.sqlモジュールと、クラスパス上の別の場所(無名モジュール)の両方から利用可能であることを検知し、「どちらを使うべきか?」という曖昧さを解消するために警告またはエラーを発します。これは、モジュールの厳密なルールにより、同じパッケージが複数のモジュールによってエクスポートされることを許さない、という原則に基づいています。この警告は、アプリケーションの予期せぬ動作やクラスロードエラーにつながる可能性があるため、適切に対処する必要があります。

「パッケージ javax.sql は複数のモジュールからアクセス可能です」の具体的な回避策

このセクションでは、「パッケージ javax.sql は複数のモジュールからアクセス可能です: <unnamed>, java.sql」という警告やエラーを解決するための具体的な手順とアプローチを解説します。この問題の根源は、同じパッケージが異なるソースから提供されていることにあるため、その重複を解消することが主な目的となります。

ステップ1: 原因の特定と依存関係の精査

まず、プロジェクト内でどのライブラリがjavax.sqlパッケージを不必要に提供しているのかを特定することが重要です。この問題はほとんどの場合、アプリケーションが直接的または間接的に依存しているライブラリが原因で発生します。

Mavenの場合: mvn dependency:tree コマンドを実行し、プロジェクトの依存関係ツリーを詳細に確認します。特に、javax.sqlという文字列で検索し、どのJARファイルがそのパッケージを含んでいるかを調べます。

Bash
mvn dependency:tree -Dincludes=javax.sql

このコマンドは、javax.sqlに関連する依存関係のみをフィルタリングして表示するわけではありませんが、出力されるツリーの中からjavax.sqlというキーワードを手動で探すことで、怪しいライブラリを特定できます。より詳細に特定のパッケージを含むJARを検索するには、IDEの機能や、JARの内容を直接確認するツール(例: jar -tf your-library.jar | grep javax/sql)を使うのが効果的です。

Gradleの場合: gradle dependencies コマンドを実行して、同様に依存関係ツリーを確認します。

Bash
gradle dependencies --configuration runtimeClasspath

ここでも、出力からjavax.sqlに関連するライブラリを探します。

一般的な原因としては、以下のようなライブラリが挙げられます。 * 古いバージョンのJDBCドライバ(一部の古いドライバはjavax.sqlを独自にバンドルしていました)。 * Java EE APIを含むJARファイル(例: geronimo-spec-jtajavax.transaction-api など、javax.sqlの関連クラスを含む可能性があるもの)。 * 一部のORMフレームワークやコネクションプーリングライブラリの古いバージョン。

ステップ2: JDK標準のjavax.sqlを優先する

ほとんどの現代的なJavaアプリケーションでは、JDK自身が提供するjavax.sqlパッケージの機能で十分です。したがって、外部ライブラリが不必要にjavax.sqlを提供している場合、そのライブラリの依存関係からjavax.sql関連のものを除外するか、より新しいバージョンのライブラリに更新することを検討します。

Mavenでの依存関係の除外(Exclusion): 特定のライブラリが不要なjavax.sql関連のクラスをバンドルしている場合、pom.xmlでその依存関係を除外することができます。例えば、あるライブラリがjavax.transactionjavax.sqlを含むgeronimo-spec-jtaを推移的依存として持ってきている場合:

Xml
<dependency> <groupId>com.example</groupId> <artifactId>your-library</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-spec-jta</artifactId> </exclusion> <!-- 必要に応じて他のjavax.*関連の除外を追加 --> </exclusions> </dependency>

Gradleでの依存関係の除外(Exclusion):

Groovy
dependencies { implementation('com.example:your-library:1.0.0') { exclude group: 'org.apache.geronimo.specs', module: 'geronimo-spec-jta' // 必要に応じて他のjavax.*関連の除外を追加 } }

ライブラリのアップグレード: もし問題のライブラリが古いバージョンである場合、その最新版が既にこの問題に対応している可能性があります。ライブラリのドキュメントを確認し、最新版へのアップグレードを試みてください。最新版では、JDKのjavax.sqlを利用するように依存関係が整理されていることが多いです。

ステップ3: module-info.javaによる明示的な依存関係の管理

アプリケーションがモジュール化されている場合(つまり、module-info.javaファイルがある場合)、javax.sqlの利用はJDKのjava.sqlモジュールに依存することを明示的に宣言します。

Java
// module-info.java module com.example.myapp { requires java.sql; // javax.sqlパッケージを利用するために必要 // 他のrequires句... }

これにより、モジュールシステムはjavax.sqljava.sqlモジュールから提供されることを認識します。もし、この宣言があってもなお「複数のモジュールからアクセス可能」という警告が出る場合、それはやはりクラスパス上に別のjavax.sql提供元が存在していることを意味します。その場合は、ステップ2の依存関係の除外がより重要になります。

ステップ4: Java EE/Jakarta EE移行の考慮 (発展的な解決策)

この問題とは直接関係しないケースもありますが、もしプロジェクトが古いJava EE APIの依存関係に強く依存しており、かつ新しいJavaバージョンのモジュールシステムで問題が発生している場合、将来的な見通しとしてJakarta EEへの移行を検討することも有効な戦略です。

Java EEは、Eclipse Foundationに移管された後、Jakarta EEとして開発が進められています。これに伴い、パッケージ名もjavax.*からjakarta.*へと変更されています(例: javax.servletjakarta.servletjavax.persistencejakarta.persistence)。

javax.sqlに関しては、Java SEの一部として残されているため、直接的にjakarta.sqlに移行する必要は通常ありません。しかし、もしアプリケーションがjavax.transactionjavax.mailなど、他のjavax.*パッケージで同様の競合問題を抱えている場合、これらをjakarta.*パッケージに移行することで、JDK標準のjavax.*との競合から解放される可能性があります。

この移行は大規模な変更を伴うため、慎重な検討と計画が必要です。しかし、モジュールシステムの複雑な問題を根本から解決する一つの道となり得ます。

ハマった点やエラー解決

この問題で特にハマりやすいのは、推移的依存性によって意図しないライブラリが持ち込まれるケースです。例えば、あなたが直接依存しているAライブラリが、さらにBライブラリに依存しており、このBライブラリが古いjavax.sqlを含むJARをバンドルしている、といった状況です。mvn dependency:treegradle dependenciesの出力は非常に長くなることがあり、どこから問題のJARが来ているのかを特定するのが困難な場合があります。

また、javax.sqlというパッケージ名自体がJDKにも存在する「特別な」パッケージであるため、単に外部ライブラリを排除するだけでなく、なぜJDKがこれを提供しているのか、という背景を理解していないと適切な解決策にたどり着きにくいこともあります。

解決策

  1. 徹底的な依存関係の調査: 上述のmvn dependency:treegradle dependenciesコマンドを駆使し、それでも特定できない場合は、プロジェクトのtarget/またはbuild/libs/ディレクトリにある最終的なJARファイルやWARファイルを展開し、内部を直接検索する(jar -tf myapp.jar | grep javax/sql)。
  2. dependencyManagementの活用(Maven): pom.xmldependencyManagementセクションで、問題のある推移的依存性のバージョンを明示的に指定したり、完全に除外したりすることで、プロジェクト全体での依存関係の解決を一貫させることができます。
  3. モジュールパスとクラスパスの理解: モジュールパス(--module-path)とクラスパス(--class-path)の違い、そして無名モジュールがどのように振る舞うかを深く理解することで、問題の根源に迫ることができます。特に、クラスパス上のJARがモジュールシステムからどのように扱われるかを把握することが重要です。
  4. 代替ライブラリの検討: もし問題のライブラリが古く、更新が望めない場合は、現代のJavaモジュールシステムに対応した代替ライブラリへの移行も視野に入れます。

これらのステップを通じて、javax.sqlパッケージに関するモジュール競合の問題を効果的に解決し、Javaアプリケーションを安定して動作させることができるようになります。

まとめ

本記事では、Java 9以降のモジュールシステムで発生する「パッケージ javax.sql は複数のモジュールからアクセス可能です: <unnamed>, java.sql」という警告やエラーについて解説しました。

  • JDK標準と外部ライブラリの競合: javax.sqlパッケージがJDKのjava.sqlモジュールによって提供される一方で、クラスパス上の外部ライブラリが同じパッケージを独自に提供することが問題の原因であることを説明しました。
  • 依存関係の精査が鍵: MavenやGradleの依存関係ツリーを詳細に分析し、どのライブラリが不必要なjavax.sqlを提供しているかを特定することが、解決への第一歩となります。
  • 解決策の適用: 不要な依存関係の除外、ライブラリのアップグレード、そしてmodule-info.javaでの明示的なrequires java.sql;の宣言が主な解決策となります。

この記事を通して、Javaモジュールシステムにおける依存関係の複雑性を理解し、javax.sqlに関連する警告やエラーを自信を持って解決できるようになったはずです。クリーンで安定したJavaアプリケーション開発のために、この知識をぜひご活用ください。

今後は、Javaモジュールシステムにおけるより高度な依存関係管理や、Jakarta EEへの移行戦略についても記事にする予定です。

参考資料