ラベル EBean の投稿を表示しています。 すべての投稿を表示
ラベル EBean の投稿を表示しています。 すべての投稿を表示

2014年3月1日土曜日

PlayFrameworkで会員制ギャラリーサイトを作る習作:Play2.2.xで@NotNullアノテーションについて調べたこと

Play Framework 2.2.1がリリースされましたね。

さて、PlayFramework 2.1で使えていた@NotNullアノテーションが2.2で開発をした時にエラーを吐くようになりました。
その解決方法についていろいろ調べて詰まったので、備忘録として控えておきます。

まず結論から言うと、
Play! 2.1 アプリを Play! 2.2 に移行した作業の覚え書き
の
@NotNullアノテーション がなくなった
の節にある以下の様な書き換えで対応するのが良いようです。

2.1
import com.avaje.ebean.validation.NotNull;
2.2
import javax.validation.constraints.NotNull;
ここに至るまでに試行錯誤した結果を書いておきます。

2.1で使えていた@NotNullアノテーションをそのまま移植

そのまま使おうとすると、ビルドの際にエラーが出るようになりました。

build.sbtにavaje-ebeanorm-apiを追記してみる

調べてみると、どうやら
avaje-ebeanorm-api is not in the list of SBT dependencies
ということだそうで、EBeanの依存関係のバグでMaven Repositoryで自動取得してくれないようなので、build.sbtに以下を追記するようなので追記してみることにしました。

  "org.avaje.ebeanorm" % "avaje-ebeanorm-api" % "3.1.1"

https://groups.google.com/forum/#!topic/play-framework/aEglzIVCnH8
https://groups.google.com/forum/#!msg/play-framework/azlPQ14XJ2I/tdOKUkYVAxAJ

build.sbtの追記をやっぱり辞める

再度ビルドすると今度は新たなエラーが

Exception in thread "main" java.lang.NoSuchMethodError: com.avaje.ebean.config.AutofetchConfig.isGarbageCollectionOnShutdown()Z

http://stackoverflow.com/questions/20520456/java-lang-nosuchmethoderror-is-chasing-me-at-cloudbees 
を見ると、
isGarbageCollectionOnShutdown
メソッドはEBean の2.6.0系には存在しないメソッドで、3.2.2で存在しているメソッドだそうです。
また、

Looks like conflicting version of same jar in classpath
とあるように、そもそもPlay Framework 2.2系で使用しているEBeanのバージョンと
avaje-ebeanorm-apiのバージョンが混在しているのがいけないっぽい。

改めて
http://cs.hatenablog.jp/entry/2013/12/15/234618
を見てみると、

2.2ではEBeanが3.2.2になっています。
とあるので、できればavaje-ebeanorm-apiも3.2.2系にできればいいんだが、
http://mvnrepository.com/artifact/org.avaje.ebeanorm/avaje-ebeanorm-api
だと、3.1.1しかリリースされてない。。。

さらにGithubを見ると、
https://github.com/ebean-orm
のリポジトリ、
deprecated-avaje-ebeanorm-api
になってる・・・。

ということで、
2.2系では

import javax.validation.constraints.NotNull;
にするという結論になりました。以上。



2014年2月26日水曜日

PlayFrameworkで会員制ギャラリーサイトを作る習作:2ページ目以降をAjaxで取得する

それでだ、
Play Frameworkで会員制ギャラリーサイトを作っている途中。
前回のところでデータを取ってきて表示するところまで行きました。
app/views/ajax/listAjax.scala.html今回は画面をスクロールしたら2ページ目、3ページ目が読み込まれるところの処理を作っていきます。

なお、今回からTypesafe Activatorでアプリケーションを起動させるのはやめました。だって、play cleanがされなくて改修前の実装コードが残ってしまうのだもの・・・・。
すなおに play debug runするようにしました。

さて、2ページ目以降を取得する処理です。

https://github.com/YoshiteruIwasaki/PlayFrameworkRecruitConsole/commit/5756fc962c1ef328e3020beb0b92d5e20c3a3b51

のコミットが全てなのですが、処理の流れを見て行きましょう。

まずはCntroller


app/controllers/ajax/ListAjaxController.java
にしました。なおパッケージ名はcontrollers/ajax/***とかcontrollers/api/*** とかのように、共通するレスポンス形式とかでまとめておいたほうが後々フィルタをかけて処理させるときとかに楽になります。

1Action1Controllerファイルくらいにしておくと大規模開発にも耐えられるようになります。(コンフリクトが起きにくくなるという意味で)

1Controllerに入れていいのはせいぜいフォームの入力・確認・完了処理くらいまでかと思います。あとは分けましょう。
デメリットとしてはコンパイルが遅くなります。
ちなみにajax/ListAjaxController.javaの名前の付け方はイケてないです。

中身の処理は引数をページ番号としてまあ結果一覧を取得して返すだけの処理です。
 
public static Result index(int page) {
   List<SiteBean> resultList = SiteBeanService.getSiteBeanResultList(page);
   return resultList.size() == 0 ? ok("") : ok(views.html.ajax.listAjax
     .render(page, resultList));
  }

テンプレートでは面倒なのでhtmlを返しています。
JSONで返して、Javascript側でゴニョゴニョしてもいいかと思います。

conf/routes
にルーティングの設定を追加しておきましょう。
 
GET     /listAjax/:page                           controllers.ajax.ListAjaxController.index(page:Integer)

次にテンプレートから呼び出す方法


さて、最初の画面を表示して2ページ目以降を取得する処理ですが、
jquery_bottom
を利用してアクションを呼び出すようにします。

ページのフッターに次に呼ぶページ番号と更に読み込むページがあるかのフラグ用の値をセットしておきます。
 
<input type="hidden" id="hasNext" value="0" />
<input type="hidden" id="page" value="1" />


あとは呼び出す処理が呼ばれた際の中身を書いていきます。

 
 $(window).on('bottom', function() {
  var obj = $(this);
  // since this ajax call might take a while
  if (!obj.data("loading") && $("#hasNext").val() == 0) {
   obj.data("loading", true);
   $("#loading").show();
   $.ajax({
    url : "/listAjax/" + $("#page").val(),
    dataType : "html",
    success : function(data) {
     if (data == "") {
      $("#hasNext").val(1);
     } else {
      $("#listContainer").append(data);
     }
     var page = parseInt( $("#page").val() );
     $("#page").val(page + 1);
     // remove the loading text
     $("#loading").hide();
     // now that the ajax call is done, we can re-enable this
     obj.data("loading", false);
     $("#list"+page).preloader();
    },

    error : function(data) {
     console.log(data);
    }
   });
  }
 });


  1. 次に呼び出すページがあるようであればページ番号を渡してAjaxリクエスト
  2. レスポンスが空だったら処理終わりのフラグを立てて終了
  3. レスポンスがあったら帰ってきたレスポンスのhtmlをappendして表示
  4. ページ番号を1追加する
  5. ローディングを隠す
  6. 画像をかっこ良く表示させるpreloaderを呼ぶ

みたいなことをやっています。

2014年2月23日日曜日

PlayFrameworkで会員制ギャラリーサイトを作る習作:EBeanを使ってデータの取得を行う

さて、それではさっそくデータを取得してトップ画面に一覧を表示する機能を作っていきます。

プロジェクトの概要はこちら

今回の機能に関するIssueはこちらになります。
https://github.com/YoshiteruIwasaki/PlayFrameworkRecruitConsole/issues/9

ロジック層

まずはデータを取得していく、MVCで言えばMのあたりから作っていきます。

modules/core/app/services/bases/SiteService.java
を作ります。

データの件数取得、データの一覧取得の条件は
getSiteCriteria
のメソッドにして共通の処理を呼ぶようにしています。

 public static PagingList<Site> getSiteCriteria() {
  return find.where().orderBy().desc("createDate")
    .findPagingList(ApplicationConfigUtils.MAX_PER_PAGE);
 }

 public static List<Site> getSiteResultList(Integer page) {
  PagingList<Site> pagingList = getSiteCriteria();
  Page<Site> currentPage = pagingList.getPage(page);
  return currentPage.getList();
 }

 public static Integer getSiteResultCount(Integer page) {
  PagingList<Site> pagingList = getSiteCriteria();
  return pagingList.getTotalPageCount();
 }


なかなかPlayFrameworkでページャーを使ってるサンプルがないのですが、このようにして呼ぶみたいです。

つぎにSiteServiceをextendsしたクラス
を作ります。
 
 public static List<SiteBean> getSiteBeanResultList(Integer page) {
  ArrayList<SiteBean> list = new ArrayList<SiteBean>();
  List<Site> resultList = getSiteResultList(page);
  for (Site site : resultList) {
   list.add(setSiteBean(site));
  }
  return list;
 }

SiteServiceはDBに直接操作を行うクラスという位置づけにしていて、
Controller側ではそのラッパーのSiteBeanServiceを呼ぶようにしています。
このクラスではDBからデータを取得する処理をSiteServiceから呼び出して、SiteBeanにセットする処理をしています。

こうすることで今後例えばキャッシュ機構を組み込む、などのようになった場合にDBアクセス層のSiteServiceはそのままで、SiteBeanServiceにキャッシュ機構を組み込めばいいようになります。

今の段階ではSiteBeanはSiteクラスをextendsしていますが、これはEBeanのModelではないため、多分extendsはやめると思います。。

JavaだとこういったModel,Bean系はsetter,getterを使うのが常ですが、Playはpublicにするのが流儀っぽいので、そういう感じにしています。

1ページに表示する件数については
modules/base/app/utils/base/ApplicationConfigUtils.java
に記述しています。こういうのは1つのサービス上統一するケースが多いと思うので、baseモジュールに書いてあります。
 
public static final int MAX_PER_PAGE = 10;

コントローラ層

 app/controllers/Application.java
のコントローラ側では
 
List resultList = SiteBeanService.getSiteBeanResultList(0);

のようにSiteBeanServiceを呼びます。
テンプレート側では
@(message: String, description: String, beanList: List[models.beans.SiteBean])

のようにしてリストを渡します。

 
<div class="container">
<div class="row">
 @if(beanList != null && beanList.size() > 0){
     @for(bean <- beanList){
    <div class="col-sm-6 col-md-4">
      <div class="thumbnail">
      <a href="@{bean.url}"><img title="@{bean.title}" src="@{bean.thumbUrl}" alt="@{bean.url}" /></a>
      </div>
    </div>
     }
 }
</div>
</div>

 でSiteBeanの一覧を表示します。
出来上がったのがこんなかんじになっています。


2014年2月21日金曜日

PlayFrameworkで会員制ギャラリーサイトを作る習作:EBeanを使用してModelの作成を行う

さてEBeanを使ってModelを作っていきます。
作成するModelの定義はこちらのIssueのとおり。


まずはModelを作成していきます。作るのはcoreのサブプロジェクト内。

https://github.com/YoshiteruIwasaki/PlayFrameworkRecruitConsole/blob/f2f8ff770ea3b3ca15a04137a0d56fe269f6c962/modules/core/app/models/bases/Site.java

@Entity
public class Site extends Model {

 private static final long serialVersionUID = 3890695880010099962L;

 @Id
 public Long siteId;

 @Required
 @NotNull
 public String title;

 @Required
 @NotNull
 @Lob
 public String url;

 @CreatedTimestamp
 public Date createDate;

 @Version
 public Date updateDate;
}

個人的にはLazyLoadとかを信頼してないので、Model同士のひも付けは自前でする派です。

urlは255文字を超える場合もあるので(企業の採用ページだったら超えることもほぼ無いはずですが)、@Lobにしています。

ちなみに、NotNullのアノテーションですが、
libraryDependencies ++= Seq(
  // Select Play modules
  javaJdbc,  // Java database API
  javaEbean, // Java Ebean plugin
  //javaJpa,   // Java JPA plugin
  javaCore,  // The core Java API
  cache,
 "org.webjars" %% "webjars-play" % "2.2.0",
 "org.webjars" % "bootstrap" % "3.1.1",
   "org.webjars" % "font-awesome" % "4.0.3",
  "mysql" % "mysql-connector-java" % "5.1.29",
  "org.avaje.ebeanorm" % "avaje-ebeanorm-api" % "3.1.1")

のようにして呼ぶ必要があります。



2013年12月7日土曜日

Play Frameworkでサブプロジェクトを使い倒す

Play framework 2.x Java and 1.x Advent Calendar 2013の7日目です。

さて今日はサブプロジェクトをうまいこと使おうよ、というお話です。
1つのプロジェクト内のcontrollerにadminディレクトリを掘っていた時期もありました。。。
運用の話はどちらかと言うと、他の人から色々聞いてみたい。。。多分自分のスクリプトはかなりしょっぱい。。。。(メモリエラー対策のため1時間に1回Jenkinsでサービスの再起動をさせている・・・)


で、今回の話はPlayのドキュメントだとWorking with sub-projectsに当たる部分になります。
Playはsbtのmulti-buildをよろしく使っているようです。
間違っている部分やもっといい方法などがあれば指摘していただけると嬉しいです。

Playは個人プロジェクトとか小規模なプロジェクトでこそ、その開発スピードなどの威力を発揮していると個人的には感じているのですが、チームで大規模な開発をしていくケースもあるかと思います。そういった場合にこのサブプロジェクトが多いに役立つのではないかと思います。



対象者によってディレクトリを区切るケース


例えばPlayで求人のマッチング系のサービスを作っていくというケースを考えてみます。
  • 求職者側は http://hoge.com/
  • 採用者側は http://hoge.com/staff
  • サービス運用者側は http://hoge.com/manager
のURLで使うケースを想定してステップごとにサブプロジェクト対応をしていく方法を考えてみます。



STEP1.ライブラリのサブプロジェクト化


Javaの開発をやっていく中でそのチームで共通的に使用しているライブラリ的な物があるかと思います。
例えば、日付のフォーマッターやUser-Agentの切り分けルール、テキストにwbrを埋め込む処理などのPlayにかかわらず使用できるライブラリです。
これらの資産をPlayでも使っていく場合に、なるべくパッケージ名は固定にしたいとか、プロジェクトに依存せずに使いたい、という要望が出てくるかと思います。

そんな時に使うのがサブプロジェクトになります。
もちろんプラグインとして利用する、jarファイルとして読み込む、などの方法も可能かと思います。ただプロジェクトを進める中で共通ライブラリも合わせて強化してフィードバックをしていく場合は、サププロジェクト化してすぐに編集できる状態にしておくのも有用かと思います。

自前ライブラリはproject/Build.scala に以下のように追記することで、Playの他のプロジェクトに依存せずに使えるようになります。
import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common)

}


やっている内容としては、

  • val common の行でサブプロジェクトの追加
  • lazy val main にdependsOn(common)で依存関係の追加

になります。
この場合のフォルダ構成は以下のようになります。

app
conf
public
modules
 └ common
    └ src
       └ main
           └ java
project
 └ Build.scala

Adding a simple library sub-projectを見てみると、サブプロジェクトにすることで、コンパイルする単位をサブプロジェクトごとに出来るようです。Java,Scalaそれぞれ100ファイルを超えてくるとコンパイルに時間がかかるので、これは地味に嬉しい・・・。まあデバッグモードだと依存関係のあるプロジェクトもビルドされ直すようですが。


STEP2.Playの拡張機能のサブプロジェクト化


Playで開発をしていると、Controllerに共通の処理を埋め込みたくなる場合があると思います。セッション系の処理をゴニョゴニョしたりサイトのtitleを出力させたりFacebook用のタグを生成したりなどなど全ページ共通処理を行う場合ですね。
そんな時にはControllerをextendsしたBaseControllerを作って、プロジェクトではそれをさらにextendして各種Controllerを呼ぶ、なんてことがあると思います(この基底になるプロジェクトをbaseプロジェクトとします)。
この場合だとsbtプロジェクトではなくなり、Playプロジェクトになるのでproject/Build.scala に以下のように記載することになります。

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

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))
  lazy val base = play.Project(appName + "-base", appVersion, appDependencies, path = file("modules/base"))

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common,base).aggregate(base)

}

lazy val baseの行にありますが、プロジェクトが Projectからplay.Projectになりました。Playに依存するプロジェクトの場合、play.Projectにする必要があります。

Playは必要最低限の準備しかしてないので、みんな独自の拡張をしていると思います。
これで自分用のPlayプロジェクトのベースが準備出来ました。
このサブプロジェクトに直接アクセスされちゃうんじゃないの?という心配ですが、routesファイルが記載されていなければ、大丈夫かと思います。

STEP3.ディレクトリ区切りのサブプロジェクト化

さて、チームで開発していく中で問題になるのが、routesファイルのコンフリクト問題です。
モジュール(controller)別で開発担当者が違う場合にroutesファイルがコンフリクトを起こしまくって色々な感情と問題を引き起します。

そんな時にサブプロジェクトにします。そうすることで多少なりともroutesファイルのコンフリクトを避ける事が出来るようになります。

先述の
  • 求職者側は http://hoge.com/
  • 採用者側は http://hoge.com/staff
  • サービス運用者側は http://hoge.com/manager
の場合、staff,managerをそれぞれサブプロジェクトにするイメージです。
この場合もサブプロジェクトはsbtのプロジェクトではなくPlayのプロジェクトになります。
・・・とここで気がつくわけです。Ebeanの処理は共通ではないだろうか・・・?と。
Modelは求職者側だろうと採用者側だろうとだろうと同じになります。プロジェクト内で共通になるModelをさらに別出ししてサブプロジェクト(この場合、coreプロジェクトとします)にします。

project/Build.scala に以下のように記載することになるかと思います。
import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))
  lazy val base = play.Project(appName + "-base", appVersion, appDependencies, path = file("modules/base"))
  lazy val core = play.Project(appName + "-core", appVersion, appDependencies, path = file("modules/core"))

  lazy val staff = play.Project(appName + "-staff", appVersion, appDependencies, path = file("modules/staff")).dependsOn(common,base,core)
  lazy val manager = play.Project(appName + "-manager", appVersion, appDependencies, path = file("modules/manager")).dependsOn(common,base,core)

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common,base,core,staff, manager).aggregate(base, core,staff, manager)

}

これでroutesファイルをサブプロジェクトごとに分割して管理できるようになりました。routesファイルもこのようにして分けることが出来るようになります。

conf/routesはこうなります。
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()

->  /staff staff.Routes
->  /manager manager.Routes

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

上記の/staff staff.Routesで指定されていた、modules/staff/conf/staff.routesはこうなります。
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.staff.Application.index()
GET     /detail/:id     controllers.staff.Detail.index(id:Long, page: Integer ?= 1)


# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

staff.routesはあらかじめprefexとして /staff がつくことがconf/routesで指定されているので、modules/staff/conf/staff.routesに記載されている
GET     /detail/:id     controllers.staff.Detail.index(id:Long, page: Integer ?= 1)
は実質的に
http://hoge.com/staff/detail/1
とかのURLに該当するようになります。

ちなみにhttp://www.playframework.com/documentation/2.1.x/SBTSubProjectsにあるように、
GET     /assets/*file               controllers.admin.Assets.at(path="/public", file)
のような書き方は試してみたもののうまく行かず。。サブプロジェクごとにAssetsを指定することで、読み込むディレクトリを切り替えられる(?)みたいです。
jQueryとかbootstrapは共通の呼び出してるからいいか・・と
GET     /assets/*file               controllers.Assets.at(path="/public", file)
のように共通の場所を呼び出す形で放置をしています。

このようにサブプロジェクトに分けることで、プロジェクトごとに開発担当者を分けて開発をしやすくなります。

最終的にディレクトリ構成はこのようになります。

app
  └ controllers
  └ models
  └ views
conf
  └ application.conf
  └ routes
modules
  └ base
    └ app/controllers
 └ common
    └ src
       └ main
           └ java
  └ core
    └ app/models
  └ manager
    └ conf/manager.routes
    └ app/controllers
    └ app/views  
  └ staff
    └ conf/staff.routes
    └ app/controllers
    └ app/views  
project
 └ build.properties
 └ Build.scala
 └ plugins.sbt


このようにサブプロジェクトにすることで、大規模開発もしやすくなるのではないかと思います。

明日は


@s_kozakeさんが 中堅SIerがPlay1系を導入した感想を とのことです。
Play1系は触ったことがないのですが、Play2系とはまた違って業務向けにはいいという話を聞くので、楽しみです。

2013年10月2日水曜日

Play FrameworkでRawSQLの実行例

Play Framework for JavaでORMにEbeanを使った際の直SQLの書き方の一例です。
ネットで探しても日本語による情報がなかなか無いので書いておきます。

public static List<DateItemBean> getTweetResultListGroupByDate(Item item) {
String sql = " SELECT created_at,"
+ " COUNT( CASE WHEN POINT = 0 THEN 1 ELSE NULL END ) AS neutral,"
+ " COUNT( CASE WHEN POINT > 0 THEN 1 ELSE NULL END ) AS positive,"
+ " COUNT( CASE WHEN POINT < 0 THEN 1 ELSE NULL END ) AS negative"
+ " FROM tweet" + " WHERE item_id = :item_id"
+ " GROUP BY DATE_FORMAT( created_at,  '%Y%m%d' )"
+ " ORDER BY created_at ASC";
List<SqlRow> sqlRows = Ebean.createSqlQuery(sql)
.setParameter("item_id", item.itemId).findList();
List<DateItemBean> results = new ArrayList<DateItemBean>();
for (SqlRow row : sqlRows) {
Date date = row.getDate("created_at");
Integer countNeutral = row.getInteger("neutral");
Integer countPositive = row.getInteger("positive");
Integer countNegative = row.getInteger("negative");
DateItemBean bean = DateItemBeanService.setDateItemBean(item,
countNeutral, countNegative, countPositive, date);
results.add(bean);
}
return results;
}



のようにしてあげることで、実行することが出来ます。 詳しいソースは以下のGithubから確認して下さい。
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/app/services/TweetService.java

SQL 内で
:item_id
のように書いてあげることでsetParameterでbindしてあげることが出来ます。

セットされた値は
row.getInteger("neutral")
などのようにして取得することが出来ます。

なおDate型で取得する場合ですが、
DATE_FORMAT( created_at,  '%Y-%m-%d 00:00:00.000' )
みたいな感じにしてもString型にしかならなかったので、オブジェクトの方を見てるのかもしれません。よくわかりませんが・・・。



2013年9月4日水曜日

Java Play framework 2.0のebeanで、Expr.containsを使ってのOR検索

一言多いプログラマーの独り言: Java Play framework 2.0のebeanで、OR検索


に触発されたので、書いてみる

public static Page page(int page, int rows, String sortBy, String order, String filter) {
    return 
        find.where("name like :keyword OR detail like :keyword")
            .setParameter("keyword", "%" + filter.trim() + "%")
            .orderBy(sortBy + " " + order)
            .findPagingList(rows)
            .getPage(page);
}

のように記載されていますが、Expr.containsを使ってこういう方法も使えるかと思います。

public static Page page(int page, int rows, String sortBy, String order, String filter) {
    return
        find.where().or(Expr.contains("name", filter.trim()), Expr.contains("detail",filter.trim()))
            .orderBy(sortBy + " " + order)
            .findPagingList(rows)
            .getPage(page);
}
http://www.avaje.org/static/javadoc/pub/com/avaje/ebean/Expr.html#contains(java.lang.String, java.lang.String)

2013年2月8日金曜日

JSONIgnoreがちょっと残念 | Play Framework2.0 ここんがんばれ!

Play Framework 2.1がリリースされましたね。 http://www.playframework.com/
さて、そんななか、Playについて叱咤激励していきます。
Java版だとORMにEBeanを使っています。

modelsの中に各モデルを記述していきます。
ここに書いた内容がDBにも反映されます。
例えば
emailを必須項目としたい場合は


@Entity
public class User extends Model {
@Required
public String email;


みたいな感じに書くわけですね。
ちなみにこの中には


 public String getEmai() {
return email + "hoge";
    }

みたいな事もかけてしまいます。
これやると、 テンプレート側で

user.getEmail()ってやったときとuser.emailってやった時で挙動が変わってしまうので、
注意が必要です。

さて、それとは別に
Modelの中には
static関数もかけてしまいます。


public static List<User> getChildren(){
return Ebean.find(User.class)
    .where()
      .eq("parentUserId", this.userId)
    .findList();
}

みたいな感じですね。
で、これを書いた状態でユーザーのリストをJSONで取ってくるなんてした時には、全てのデータに対してstatic関数の中身も処理した上でJSONを返すため、
必要でないデータは意図的に@JsonIgnoreする必要があります。

おそらくModelの中にはstatic関数は書かなくて別の所で処理したほうがいいんでしょうが・・・。
(phpで使ってたPropelだとPeerがあったし、Slim3だとServiceがあるんだけどなあ・・・)

2013年1月8日火曜日

Play Framework2.0 でSQLを出力する方法

Play FrameworkのJava版ではORMにEbeanが使えるのですが、どんなSQLが発行されているか、確認をしたいケースがあります。

そんな時には、application.confに以下のように追記することで、コンソールおよびログファイルに実際に発行されているSQLが出力されるようになります。


db.default.logStatements=true
logger.com.jolbox=DEBUG

Stackoverflowに載ってました。
http://stackoverflow.com/questions/9719601/play-framework-2-0-and-ebean-sql-logging