はじめに
JavaでDBを使ったWebアプリを作るにあたって現時点で最もシンプルで後々後悔しなさそうな構成ってどんなんだろうと考えたときに、JAX-RSとJPAというのが今のところよさそうなので、その簡単なサンプルを試してみました。
JAX-RSはJersey、JPAはEclipseLinkとHibernateを試してます。
構成
コードはこちらに
https://github.com/kamegu/jersey-jpa/tree/eclipselink-simple
eclipselink-sampleタグが今回の対象です。
依存ライブラリ
まずは依存ライブラリの登録です。
今回は調査の都合上Mavenのマルチモジュール構成にしましたが、普通のMavenプロジェクトをつくってpom.xmlに依存ライブラリを追加すればOKです。
今回は現時点で最新版のJersey2.15、servletコンテナとしてTomcat8を使ったのでJersey User Guideを参考にJerseyのライブラリを追加してます。
また、JPAとしてEclipseLinkを追加してます。DBはMySQLを使用しました。
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.15</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
</dependencies>
データベース
MySQLサーバを立ててDB名:demoで次のようなテーブルを作ってデータを何件か作成しておきます。
CREATE TABLE `customer` (
`id` bigint(20) NOT NULL PRIMARY KEY,
`kname1` varchar(64) NOT NULL,
`kname2` varchar(64) NOT NULL
)
Jerseyを使う
次の2つのクラスを作っただけです。
package com.github.kamegu.jerseyjpa.web.resources;
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages(false, this.getClass().getPackage().getName() + ".app");
}
}
package com.github.kamegu.jerseyjpa.web.resources.app;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("")
public class IndexResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getText() {
return "test";
}
}
このあたりの説明が分かりやすいかと思います。
http://www.coppermine.jp/docs/programming/2012/12/jaxrs1.html
上の例では、AppConfigクラスが自動で読み込まれ、そこで指定しているパッケージ内にあるIndexResourceクラスがリソースとして利用可能になります。
例えばmvn packageでwarを作ってTomcatで起動して次のようなリクエストをすると"test"と表示されるはずです。
http://localhost:8080/jersey-jpa-web/
実際にJPAを使う
JPAについては、このへんを見てもらうとなんとなく分かると思います。
http://vividcode.hatenablog.com/entry/java/jpa-introduction
エンティティクラス
JPAで利用するエンティティクラスを作成します。上で作成したcustomerテーブルに対応するクラスを作成してます。これは一番シンプルな例で、JPAではこれ以外にもいろいろなアノテーションが用意されているので調べても面白いと思います。
@Entity
public class Customer {
@Id
private Integer id;
@Column
private String kname1;
@Column
private String kname2;
}
JPA用設定ファイル
JPAを利用するためにはまずsrc/main/resources/META-INF/にpersistence.xmlを作成します。
今回は次のようにしています。
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="demo" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://192.168.102.228/demo"/>
<property name="javax.persistence.jdbc.user" value="demo"/>
<property name="javax.persistence.jdbc.password" value="demo"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>
</persistence>
persistence-unit要素でPersistenceUnit(永続ユニット)を定義します。上ではユニット名を"demo"としました。また、properties要素でDBへの接続情報を設定しています。各パラメータは環境に応じて変更する必要があります。
persistence.xml内でエンティティクラスをclass
要素で追加するのが間違いない方法ですが、今回はexclude-unlisted-classes
要素をfalseに設定することで、自動で探しだされたものが追加されるようにしています。
※ 仕様を読む限り、これはJava SE環境において必ずしも保障されている方法ではないかもしれません(詳細未確認)
JPAでアクセス
JPAではEntityManagerというものから操作を行います。
EntityManagerの取得(生成)方法はJavaEE環境かどうかで異なります。
JavaEE環境では次のように取得します。
@PersistenceContext(unitName = "demo")
private EntityManager em;
unitNameはpersistence.xmlで指定した名前です。
それ以外の環境(今回もこっち)では次のように取得します
EntityManagerFactory fac = Persistence.createEntityManagerFactory("demo");
EntityManager em = fac.createEntityManager();
データ取得
データ取得は次のように行います。今回はJPQLというJPA上のSQLライクなクエリ言語を使用しています。
List<Customer> customers = em.createQuery("SELECT c FROM Customer c", Customer.class).getResultList();
おまけ
今回はwebモジュールとentityモジュールに分けて前者が後者に依存するようにしておいて、両方にpersistence.xmlとエンティティクラスを作成しました。
それらを組み合わせてデータ取得を試みた結果、
-
persistenceUnit@entity
モジュール -> エンティティ@entity
モジュール => OK -
persistenceUnit@entity
モジュール -> エンティティ@web
モジュール => NG -
persistenceUnit@web
モジュール -> エンティティ@entity
モジュール => NG -
persistenceUnit@web
モジュール -> エンティティ@web
モジュール => OK
となりました。つまり、エンティティクラスはpersistence.xmlと同じモジュール内にあれば利用可能ということ。
なお、webモジュールのpersistence.xmlに
<class>com.github.kamegu.jerseyjpa.entity.Customer</class>
を追加すると、3つ目のケースもOKになりました。
また、hibernateでのコードもhibernate-simpleタグで参照可能です。
pom.xmlとpersistence.xmlを変更しているだけです。