PlayFramework2.1.3(Java)+JPA 使ってみる #1

Play 2.x RoadmapによるとPlayFramework2.3(Java)では、EBeanでなくJPAが推奨されるようです。職場ではplay1を使っているが、play2でJPAを使えるなら移行しやすいかもしれない。2.3を待たずPlayFramework2.1.3(Java)+JPAでどれだけやれるか試そう。と思った。
以下は、play2.1(Java)でJPAを使ってみた時のメモです。

アプリケーション作成

Play のインストールのつぎにアプリケーション作成する。

$play2 new jpaprac1

コマンドも結構変わってるみたいね。playコマンドでplayコンソールに入り、実行するコマンドもあるようだ。

Play コンソールを使う

ディレクトリ構成はplay1と似てる。projectディレクトリとtargetディレクトリが増えたな。

Play アプリケーションの構造

JNDI を経由してデータソースを公開する

play2でJPAが使えるかどうかを検証することが目的なので、コントローラー、ビューは飛ばしてモデル周りを見ていく。
ドキュメント通りにやる。

conf/application.conf

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.jndiName=DefaultDS
jpa.default=defaultPersistenceUnit

[2013.8.30] 追記 ドキュメントに書いてないけど `jpa.default=defaultPersistenceUnit` も追加する。

play1から見慣れた application.conf な感じ。play2はマルチDBがサポートされたので default は接続先名なんだろう。

設定ファイルのシンタックスと機能

フレームワークIDははくなった、といつかどこかで聞いた気がする。

JPA の実装をプロジェクトに追加する

play2.1はJPA関連に依存していないので、使用ライブラリに追加する必要があると。

ドキュメントに書いて無くてちょっと躓いたが、これは project/Build.scala を編集する。

samples/java/computer-database-jpa の project/Build.scala

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

    val appName         = "computer-database-jpa"
    val appVersion      = "1.0"

    val appDependencies = Seq(
      javaCore,
      javaJdbc,
      javaJpa,
      "org.hibernate" % "hibernate-entitymanager" % "3.6.9.Final"
    )

    val main = play.Project(appName, appVersion, appDependencies).settings(
      ebeanEnabled := false
    )
}

依存ライブラリは conf/dependencies.yml でなくここに書くことになったのか。conf/application.conf に書いてた name, version もここに書くと。

Build.scala を編集したらplay reload (または playコンソールでreloadコマンド)する。と、次回testしたとき依存ライブラリが解決されるようだ。
runのときは「auto-reloading」してくれる。とりあえず reload コマンドは覚えておこう。ライブラリを追加したら play eclipse もしておくとよさそうだ。

ビルドシステム
ライブラリ依存性の管理

永続性ユニットを作成する

ドキュメント通りにやる。
samples/java/computer-database-jpa の persistence.xml をコピペでもいい。

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <non-jta-data-source>DefaultDS</non-jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
        </properties>
    </persistence-unit>

</persistence>

play.db.jpa.JPA ヘルパーを使う

ドキュメント通りにやる。あるいは samples/java/computer-database-jpa の Company.java を参考にする。

@Entity
public class Company {

    @Id
    public Long id;

    @Constraints.Required
    public String name;

    public static Company findById(Long id) {
        return JPA.em().find(Company.class, id);
    }

    public void save() {
        JPA.em().persist(this);
    }
}

play2.1.3 にはJPA用のModelクラスがないので、そのうち自作する必要がありそうだ。

ユニットテスト(できなかった)

アプリケーションのテスト

DDLも作っていないが、ここまでをテストしてみる。DBに接続できない、テーブルがないくらいは出るだろうか。
samples/java/computer-database-jpa の ModelTest.java を参考にした。

public class ModelTest
{
    @Test
    public void findById() {
        running(fakeApplication(), new Runnable() {
           public void run() {
               JPA.withTransaction(new play.libs.F.Callback0() {
                   public void invoke() {
                       Company asdf = Company.findById(1L);
                       assertThat(asdf.name).isEqualTo("asdf");
                   }
               });
           }
        });
    }
}

これが噂の fakeApplication か。これはうざい。
play2のテスト実行はplayコンソールで test。しかし、テストされずスルーされてしまった。

[info] ModelTest
[info]
[info]
[info] Total for test ModelTest
[info] Finished in 0.001 seconds
[info] 0 tests, 0 failures, 0 errors
[info] Passed: : Total 0, Failed 0, Errors 0, Passed 0, Skipped 0

テストクラスは拾われたが、メソッドがスルーされてる。@Testアノテートしているのになぜだろう。
Eclipseからテストしたらテストできたようだ。これはあとで調査しよう。

ユニットテストできない問題

この issue のようです。

回避方法はここで。