ぺんぎんらぼ

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です。たまに絵を描きます。

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です

Eclipseで始めるJakarta EE 10プログラミング超入門 ③ - JPAアプリケーションの作成

データベースにアクセスするアプリケーションを作るとき、どのような方法でデータベースアクセスを実装してますか?やっぱりMyBatis?まさかのJDBC
Jakarta EEではデータベースアクセスのフレームワークとしてJPA (Jakarta Persistence PI) というものが用意されています。日本では残念ながら知名度が低いフレームワークです。

今回は、この「JPA」を使って、データベースから取得した値を画面に表示するWebアプリケーションを作成します。
なんと、Javaクラスを3つ、設定ファイルを2つ作成するだけなので、ぜひ、Jakarta EEによるJPAアプリケーションを体験してみてください。

アプリケーションの作成

プロジェクトの用意

Jakarta EEのプロジェクトが必要です。以前の記事を参考にプロジェクトを用意してください。

penguinlabo.hatenablog.com

Webアプリケーションサーバの用意

eclipse上でWebアプリケーションサーバを立ち上げるための設定をします。
ほとんどのWebアプリケーションサーバはzipファイルを展開して、eclipse上にサーバ・ランタイムを登録するだけです。

エンティティの作成

エンティティとは、データベースのテーブルと一対一となるクラスです。

  • テーブル = エンティティクラス
  • テーブルの列 = エンティティクラスのフィールド

という形でクラスを作成します。
今回は以下のテーブルに対応するエンティティクラスを作成します。

テーブル名 : profile

列名 列型 属性
id 数値 主キー
name 文字列 非NULL値
birthday 日付

このテーブルのエンティティクラスは以下のようになります。

import java.io.Serializable;
import java.time.LocalDate;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;

@Entity
public class Profile implements Serializable {
    @Id
    private int id;
    
    @NotNull
    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値の列に対応するフィールドに@NotNullアノテーションを指定します。

データベースアクセスの作成

データベースにアクセスするクラスを作成します。Jakarta EEではEJBというクラスで作成しますが、今回はEJBとは何か?という部分は理解しなくでもよいです。

import jakarta.ejb.Stateless;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@Stateless
public class ProfileBean {
    @PersistenceContext
    private EntityManager em;

    public Profile findById(int id) {
        return em.find(Profile.class, id);
    }
}

今回はfindByIdメソッドとして、主キーで検索するメソッドを実装しています。
EntityManager型のフィールドを定義して、そのフィールドに@PersistenceContextアノテーションを指定します。こうすることで、JPAがデータベースアクセスを肩代わりしてくれるEntityManagerが作成されます。
そして、メソッド内でEntityManagerのfindメソッドを呼び出すことで、主キーによるテーブルアクセスが実行されます。

サーブレットの作成

以上でデータベースにアクセスするクラスの作成は完了です。
最後に、これまでのクラスを使ってデータベースから値を取得して、Webブラウザに表示するためのサーブレットクラスを作成します。

import java.io.IOException;

import com.sandbox.jakarta.web.ejb.ProfileBean;

import jakarta.ejb.EJB;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @EJB
    private ProfileBean profileBean;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.getOutputStream().print("Hello " + profileBean.findById(1).getName());
    }
}

主キーを使ってテーブルからレコードを取得する部分は、

profileBean.findById(1)

です。引数の「1」が検索する主キーの値で、復帰値はレコードの内容が格納されたProfileクラスになります。

設定ファイルの作成

Jakaera EE 10 まではpersistence.xmlという設定ファイルが必須になります。
このファイルはMETA-INFフォルダに作成する必要があります。src/main/resourcesMETA-INFフォルダを作成して、そのフォルダにpersistence.xmlファイルを作成します。

次のバージョンとなるJakarta Persistence 3.2では、PersistenceConfigurationというクラスを使って、設定ファイルレスでJPAが使えるようになる予定です。
指定しているスキーマファイル「persistence_3_0.xsd」やversion属性が3.0と古いバージョンになっていますが、3.1用のスキーマファイルが提供されておらず、3.1でもpersistence.xmlの内容に変更がないため、3.0を指定しています。
<?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="JTA"> 
        <properties>
            <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>

本来であれば、persistence.xmlに接続先データベースのデータソース名や接続情報を記載しますが、今回はお試しなので、JPAがデフォルトで持っているインメモリデータベースのH2を使用します。
接続情報を記載しなかった場合、このH2データベースが使用されます。

アプリケーションサーバWildFly、PayaraはデフォルトでH2データベースが使用されますが、WebSphere LibertyはデフォルトのJNDI名に対応するデータソースが定義されていないというエラーが発生します。 デフォルトでH2データベースが使用されないアプリケーションサーバはデータソースの定義とJNDI名の指定が必要になります。

今回のお試し実装では2つのプロパティを設定しています。

<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.xmljakarta.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です。

作成完了!!

以上で、必要なファイルの作成は終わりです。
以下のようなファイル配置になります。

アプリケーションの動作確認

作成したアプリケーションをWebアプリケーションサーバに追加して、Webアプリケーションサーバを起動します。

Webアプリケーションサーバによってポートが違ったり、プロジェクトによってコンテキストルートが変わったりしますが、ほとんどのケースでは以下のURLをWebブラウザで開けば、作成したServletアプリケーションが動作して結果が表示されます。

http://localhost:8080/プロジェクト名/hello

正しく動作していれば、以下のような画面が表示されます。