データベースにアクセスするアプリケーションを作るとき、どのような方法でデータベースアクセスを実装してますか?やっぱりMyBatis?まさかのJDBC?
Jakarta EEではデータベースアクセスのフレームワークとしてJPA (Jakarta Persistence PI) というものが用意されています。日本では残念ながら知名度が低いフレームワークです。
今回は、この「JPA」を使って、データベースから取得した値を画面に表示するコンソールアプリケーションを作成します。
なんと、Javaクラスを2つ、設定ファイルを2つ作成するだけなので、ぜひ、Java SEによるJPAアプリケーションを体験してみてください。
アプリケーションの作成
Gradleプロジェクトの作成
EclipseのBuildshipプラグインを使って、Gradleプロジェクトを作成します。
プロジェクト名を入力するだけで、数クリックでプロジェクトが作成できます。
検証目的のプロジェクトなので、フラットなシングルプロジェクトとして作成するのもよいでしょう。
Buildshipプラグインで、Gradleのシングルプロジェクトを作成する方法は、以下の記事で解説しています。
build.gradleの編集
build.gradleを編集して、Jakarta EEのプロジェクトとして必要な設定を記載します。
plugins { id 'java-library' } apply plugin: 'eclipse' sourceCompatibility = '17' targetCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.hibernate:hibernate-core:6.4.4.Final' implementation 'com.h2database:h2:2.2.224' }
依存関係の設定
dependencies { implementation 'org.hibernate:hibernate-core:6.4.4.Final' implementation 'com.h2database:h2:2.2.224' }
JPAのライブラリを依存関係に追加します。
ここで指定しているライブラリはJPAの実装となるhibernateを指定しています。
また、今回はインメモリデータベースであるH2を使うので、H2のJDBCドライバも依存関係に追加しています。
Gradleプロジェクトのリフレッシュ
build.gradleファイルの変更内容をプロジェクトに反映する必要があります。
プロジェクトを右クリックして、[Gradle] - [Gradleプロジェクトのリフレッシュ]を実行します。
「JREシステム・ライブラリー」がbuild.gradleファイルで指定したバージョンのJavaになっていること、「プロジェクトと外部の依存関係」にhibernateのライブラリとH2のJDBCドライバが追加されていることが確認できます。
エンティティの作成
エンティティとは、データベースのテーブルと一対一となるクラスです。
- テーブル = エンティティクラス
- テーブルの列 = エンティティクラスのフィールド
という形でクラスを作成します。
今回は以下のテーブルに対応するエンティティクラスを作成します。
テーブル名 : profile
列名 | 列型 | 属性 |
---|---|---|
id | 数値 | 主キー |
name | 文字列 | 非NULL値 |
birthday | 日付 |
このテーブルのエンティティクラスは以下のようになります。
import java.io.Serializable; import java.time.LocalDate; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity public class Profile implements Serializable { @Id private int id; @Column(nullable = false) private String name; private LocalDate birthday; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public LocalDate getBirthday() { return birthday; } public void setBirthday(LocalDate birthday) { this.birthday = birthday; } }
クラスは単純なPOJPと同様で、フィールドとフィールドのGetter、Setterの実装です。
エンティティの実装方法はクラス名をテーブル名、フィールド名を列名と同じ名前にします。
そして、クラスに@Entity
アノテーションを、主キーに対応するフィールドに@Id
アノテーションを指定します。
また、非NULL値の列に対応するフィールドに@Column(nullable = false)
アノテーションを指定します。
mainメソッドの作成
データベースにアクセスするmainメソッドを実装したクラスを作成します。
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; public class Main { public static void main(String[] args) { try (EntityManagerFactory emf = Persistence.createEntityManagerFactory("defaultPU"); EntityManager em = emf.createEntityManager()) { em.getTransaction().begin(); Profile entity = em.find(Profile.class, 1); System.out.println("Hello " + entity.getName()); em.getTransaction().commit(); } } }
主キーを使ってテーブルからレコードを取得する部分は、
em.find(Profile.class, 1)
です。引数の「1」が検索する主キーの値で、復帰値はレコードの内容が格納されたProfileクラスになります。
設定ファイルの作成
Jakarta Persistence 3.1 まではpersistence.xml
という設定ファイルが必須になります。
このファイルはMETA-INF
フォルダに作成する必要があります。src/main/resources
にMETA-INF
フォルダを作成して、そのフォルダにpersistence.xml
ファイルを作成します。
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd" version="3.0"> <persistence-unit name="defaultPU" transaction-type="RESOURCE_LOCAL"> <properties> <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:test" /> <property name="jakarta.persistence.jdbc.user" value="sa" /> <property name="jakarta.persistence.jdbc.password" value="" /> <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/> <property name="jakarta.persistence.sql-load-script-source" value="data.sql"/> </properties> </persistence-unit> </persistence>
今回のお試し実装ではデータベースの接続情報と2つのプロパティを設定しています。
<property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:test" /> <property name="jakarta.persistence.jdbc.user" value="sa" /> <property name="jakarta.persistence.jdbc.password" value="" />
インメモリデータベースであるH2の接続情報を定義しています。他のデータベースに接続する場合は、この部分を変更します。
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
起動時にデータベースに対するアクションです。エンティティクラスに対応するテーブルを削除(drop)して、エンティティクラスの定義内容に従ってテーブルを作成(create)する設定です。
<property name="jakarta.persistence.sql-load-script-source" value="data.sql"/>
データ読み込み用のスクリプトファイルからDML文を読み込んで実行します。基本的に、このスクリプトファイルにテーブルに挿入するSQL文を記載して、テーブルを初期化します。
データ読み込み用のスクリプトの作成
先ほど作成したpersistence.xml
のjakarta.persistence.sql-load-script-source
で定義したdata.sql
ファイルを作成します。
ファイル名のみでパスが指定されていないので、src/main/resources
直下に配置する必要があります。
INSERT INTO profile (id, name, birthday) VALUES (1, 'Matsuki', '2021-12-31')
profileテーブルに主キー列id
に値が「1」のレコードをINSERTするSQLです。
作成完了!!
以上で、必要なファイルの作成は終わりです。
以下のようなファイル配置になります。
アプリケーションの動作確認
作成したアプリケーションを実行します。
正しく動作していれば、コンソールに以下のように表示されます。
2月 18, 2024 8:14:37 午前 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation INFO: HHH000204: Processing PersistenceUnitInfo [name: defaultPU] 2月 18, 2024 8:14:37 午前 org.hibernate.Version logVersion INFO: HHH000412: Hibernate ORM core version 6.4.4.Final 2月 18, 2024 8:14:37 午前 org.hibernate.cache.internal.RegionFactoryInitiator initiateService INFO: HHH000026: Second-level cache disabled 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using built-in connection pool (not intended for production use) 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: Loaded JDBC driver class: org.h2.Driver 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001012: Connecting with JDBC URL [jdbc:h2:mem:test] 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {password=****, user=sa} 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false 2月 18, 2024 8:14:37 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init> INFO: HHH10001115: Connection pool size: 20 (min=1) 2月 18, 2024 8:14:39 午前 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService INFO: HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2月 18, 2024 8:14:39 午前 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@2007435e] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode. 2月 18, 2024 8:14:39 午前 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@16a2ed51] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode. Hello Matsuki 2月 18, 2024 8:14:39 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PoolState stop INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:mem:test]