環境
やったこと
新規アプリケーションの作成
playを解凍したディレクトリに移動して、下記のように play new [新規アプリケーション名]と打つ。
play new foobar
What is the application name?はとりあえずそのままで良いので空のままEnter.
Eclipseプロジェクトへの変換
サンプル動かす程度なら別にIDEの形式にする必要もないのだけど、気分的な問題なのでやる。
コマンドプロンプトでさっき作ったfoobarのディレクトリに移動して、playを実行*1してplay consoleを起動する。
で、eclipseコマンドを実行する。
eclipse
Eclipseのimportから、さっきのfoobarディレクトリをコピー&インポートする。
Webアプリケーションの起動
Eclipseにインポートしたプロジェクトでplay consoleを起動する。んで、runコマンドでサーバを起動する。
run
http://localhost:9000/ にアクセスする。
Welcome to Play 2.1
さっきの画面にファーストステップ的なカンタンなガイドが書いてあるので、それを読んでいく。
まず、http://localhost:9000/ にアクセスしたとき、最初に呼ばれるcontrollerはどう定義されているかについて。conf/routesファイルに定義してある。
# Home page GET / controllers.Application.index()
という定義があり、"/"にアクセスすると、controllers.Applicationクラスのindexメソッドが呼ばれる、と書いてある。んで、そのindexメソッドはこうなっている。
public static Result index() { return ok(index.render("Your new application is ready.")); }
ドキュメントによると「このメソッドはHTTPリクエストを受け取ってHTTPレスポンスを返す。ここでは200 OKを返し、テンプレートにテキストを埋める」と書いてある。ヒジョーにアレな言い方をすればStrutsのAction的なもんなんでしょう。
で、ビュー側のテンプレートはapp/views/index.scala.htmlファイル。このファイルはJavaのフツーのクラスファイルとしてコンパイルされる、って書いてある。
@(message: String) @main("Welcome to Play 2.1") { @play20.welcome(message, style = "Java") }
↑の一行目は、テンプレートに対する関数の引数、とある。messageて名前のString型のパラメータが一つありますよ、と。で、mainていう別のテンプレートの関数を呼んでいる。そのファイルはapp/views/main.scala.htmlでHTMLのテンプレートが書いてありますよ、と。
なお、scalaっていきなり出てくるけどJavaと互換性あるしJavaとそんなに違うもんでもないしそーいうもんなので混乱するなや、とNoteに書いてある。HTMLとscalaのミックスがplayのviewの特徴、ってことなんでしょう。
Eclipseでデバッグ
Scala+Play 2.0でWebアプリ開発入門(3):便利なPlayコンソールとEclipseでのデバッグ方法 (3/3) - @ITに書いてあることのコピペだけども。
まず、play consoleをデバッグモードで起動してサーバーを起動する。コマンドは、play debug run もちろんplay consoleをplay debugで起動したあと、runでサーバを起動しても良い。下記スクショはplay debug runしたところ。
eclipseに移って、Run -> Debug Configurations -> Remote Java Applicationでnewして下記スクショのように入力する。ポートを9999にするくらいかな。デバッグモードのスクショにもあるとおり、デフォルトではポート9999でソケットを待ち受けている。
あとはいつものデバッグモード。Scalaのコードもstep inとかでちゃんと止まってくれるっぽいんですかね?
依存性の解決
とりあえず最低限必要なことは、SBTDependenciesに書いてあります。
↑のドキュメントによると、外部のライブラリを使用する方法は二種類ある。一つは、/libディレクトリにjarを置くだけ。このエントリでは、下の「JDBC接続」のところでやってます。もう一つは、sbtを使う方法。ごくカンタンに言えば、Mavenのdependency追加するヤツと同様のもの。
まず、project/Build.scalaに依存モジュールを追加する。下記ではサンプルとしてpoiとfastutilを入れている。Maven Repository: Search/Browse/Explore で検索して、SBTのタブに入ってるヤツを使う。
val appDependencies = Seq( // Add your project dependencies here, javaCore, javaJdbc, javaEbean, "org.apache.poi" % "poi" % "3.9", "fastutil" % "fastutil" % "5.0.9" )
んで、play consoleで、設定ファイルをリロードするためにreload、依存性解決のためにupdate、Eclipseプロジェクトの設定ファイル(.classpath)書き換えのためにeclipse、を実行する。
なお、eclipseコマンドでEclipseプロジェクトの設定ファイルが書き換えられるので、安定を求める人はEclipseを一旦落としておいたほうが良い。
reload update eclipse
下記スクショは上記コマンドの実行例。fastutilしかダウンロードが行われていないけど、poiは既にダウンロード済みなため出ていないです。
依存性を消したい場合は、project/Build.scalaから該当のモジュールを消してreload,update,eclipseする。
JDBC接続
とりあえず、ナマのJDBCでアクセスしてみる。使うDBはいつものようにOracle 11g XE。playの流儀では開発中はH2使ってねって感じみたいだけども、ひとまずソレは置いておく。なお、modelを見ながらやりました。
まず、プロジェクトのディレクトリに/libを作ってそこにJDBCドライバのojdbc6.jarをコピーする。
conf/application.confにDB接続情報を書く。
db.default.driver=oracle.jdbc.OracleDriver db.default.url="jdbc:oracle:thin:@192.168.0.12:1521:XE" db.default.user=kagamihoge db.default.password=xxxx
play.db.DB.getConnection()からjava.sql.Connectionのインスタンスが取得できる。下記は、modelとか考えるめんどくさかったんで一先ずサンプルのcontrollers.ApplicationにJDBCアクセスのコードを追加したところ。
package controllers; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import play.db.DB; import play.mvc.Controller; import play.mvc.Result; import views.html.index; public class Application extends Controller { public static Result index() { try (Connection connection = DB.getConnection(); PreparedStatement sql = connection.prepareStatement("SELECT * FROM SRC WHERE ROWNUM < 100")) { ResultSet resultSet = sql.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getString(1)); } } catch (SQLException e) { e.printStackTrace(); } return ok(index.render("Your new application is ready.")); } }
とりあえず、そんだけ。
JPA(Hibernate)
JavaJPAを見ながらやりました。
conf/application.confを編集する。JNDIの設定と、JPAのpersistence-unitとの関連付けを書く。
db.default.driver=oracle.jdbc.OracleDriver db.default.url="jdbc:oracle:thin:@192.168.0.12:1521:XE" db.default.user=kagamihoge db.default.password=xxxx db.default.jndiName=DefaultDS jpa.default=defaultPersistenceUnit
project/Build.scalaを編集する。javaJpaと、JPAの実体であるHibernateの依存性を追加する。
val appDependencies = Seq( // Add your project dependencies here, javaCore, javaJdbc, javaJpa, "org.hibernate" % "hibernate-entitymanager" % "4.2.1.Final" )
conf/META-INF/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.Oracle10gDialect" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
Oracleにテキトーなテーブルを作る。
create table dest ( column1 varchar2(16) , column2 varchar2(16) );
Modelとなるクラスを作る。play.db.jpa.JPA#emでEntityManagerインスタンス取得してからは、いつものJPA。
package models; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.Id; import play.db.jpa.JPA; @Entity public class Dest { @Id public String column1; @Column public String column2; public static Dest findById(String id) { EntityManager em = JPA.em(); return em.find(Dest.class, id); } }
Contollerのアクションメソッドに@play.db.jpa.Transactionalを付けて上記のModelを使う。詳細はドキュメントに譲るし俺自身よくわかってないが、とにかくTransactionalをつけないといけない。
public class Application extends Controller { @Transactional public static Result index() { Dest dest = Dest.findById("0000000000000001"); System.out.println(dest.column1 + ":" + dest.column2);; return ok(index.render("Your new application is ready.")); } }
これで http://localhost:9000/ にアクセスすると、play consoleにこんな感じで表示される。
[info] play - datasource [jdbc:oracle:thin:@192.168.0.12:1521:XE] bound to JNDIas DefaultDS [info] play - database [default] connected at jdbc:oracle:thin:@192.168.0.12:1521:XE [info] play - Application started (Dev) Hibernate: select dest0_.column1 as column1_0_0_, dest0_.column2 as column2_0_0_ from Dest dest0_ where dest0_.column1=? 0000000000000001:VYjJXvPElDXMpCnr
以下、JPA使うときのハマりどころなど。
・play.db.jpa.JPA can not be resolved.
Build.scala のappDependenciesにjavaJpaを追加する。
・No EntityManager bound to this thread. Try to annotate your action method with @play.db.jpa.Transactional
Controllerのアクションメソッド(index()とか)に@play.db.jpa.Transactionalをつける。
・RuntimeException: No JPA EntityManagerFactory configured for name [default]
application.confにjpa.default=defaultPersistenceUnitを追加する。persistence-unitの名前は適宜読み替え。
・ORA-00911 が発生する。
Hibernate: select dest0_.column1 as column1_0_0_, dest0_._ebean_intercept as col
umn2_0_0_, dest0_.column2 as column3_0_0_ from Dest dest0_ where dest0_.column1=
?
[error] o.h.e.j.s.SqlExceptionHelper - ORA-00911:
Build.scala のappDependenciesからjavaEbeanを削除する。
・RuntimeException: java.lang.NoClassDefFoundError: com/avaje/ebean/bean/EntityBean
javaのソース適当にいじって何回かRELOADしてたら出なくなった。まぁebeanの設定消した関係で何かが残存してただけと思うけど。