Weitere ähnliche Inhalte
Ähnlich wie Play meetup-2-dev-best-practices
Ähnlich wie Play meetup-2-dev-best-practices (20)
Play meetup-2-dev-best-practices
- 21. 開発環境
CI は CircleCI を使用
• Play (sbt) も当然使える。
• 主要なDBやキャッシュサーバーが
最初から起動している。
• 小規模プロジェクトなら無料プラ
ンで十分。
- 24. 開発環境
DB周りのポイント - テスト
• No inMemoryDatabase
• あまりテストにならない。
• 違いを考慮したテストを書く必要があったり、
本末転倒。
• DBを使うテストは並列にしない。
• sbt でテストを動かす際の、3つのレベルの
並列性に注意。
- 25. 開発環境
DB周りのポイント - マイグレーション
• Evolutions ではなく Flyway を使う。
• Flyway はシンプルにSQLで書けてよい。
• evolutions の嫌なところ。
• ファイル名が <数字>.sql
• よく inconsistent state になる。
- 26. 開発環境
DB周りのポイント - マイグレーション
• Playアプリケーション起動時に勝手に起
動する系は地雷。
• 設定でオフにしても、テスト時の
FakeApplication で起動したりする。
• コマンドで手動実行、あるいはデプロイ
スクリプトに組み込む。
- 27. 開発環境
DB周りのポイント - Flyway
• ファイル名は数字ではなくてタイム
スタンプにする。
• 番号がかぶるのを避ける。
• 例:V201507051534__Create_foo.sql
• outOfOrder を有効にする。
- 28. 開発環境
DB周りのポイント - Flyway
なぜ play-flyway ではなくて
Flyway SBT plugin なのか?
• 後述のコード生成と相性が悪い。
• そもそも、起動するタイミングとかコン
トロールしたいし、プラグインを使う意
味があまりない。
- 30. 開発環境
/** Entity class storing rows of table Conversation
* @param id Database column id DBType(serial), AutoInc, PrimaryKey
* @param dataSourceId Database column data_source_id DBType(int2)
* @param lastUpdatedAt Database column last_updated_at DBType(timestamp)
* @param status Database column status DBType(int2) */
case class ConversationRow(id: Int, dataSourceId: Short, lastUpdatedAt: DateTime, status: Short)
/** GetResult implicit for fetching ConversationRow objects using plain SQL queries */
implicit def GetResultConversationRow(implicit e0: GR[Int], e1: GR[Short], e2: GR[DateTime]): GR[ConversationRow] = GR{
prs => import prs._
ConversationRow.tupled((<<[Int], <<[Short], <<[DateTime], <<[Short]))
}
/** Table description of table conversation. Objects of this class serve as prototypes for rows in queries. */
class Conversation(_tableTag: Tag) extends Table[ConversationRow](_tableTag, "conversation") {
def * = (id, dataSourceId, lastUpdatedAt, status) <> (ConversationRow.tupled, ConversationRow.unapply)
/** Maps whole row to an option. Useful for outer joins. */
def ? = (id.?, dataSourceId.?, lastUpdatedAt.?, status.?).shaped.<>({r=>import r._; _1.map(_=>
ConversationRow.tupled((_1.get, _2.get, _3.get, _4.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not
supported."))
/** Database column id DBType(serial), AutoInc, PrimaryKey */
val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
/** Database column data_source_id DBType(int2) */
val dataSourceId: Column[Short] = column[Short]("data_source_id")
/** Database column last_updated_at DBType(timestamp) */
val lastUpdatedAt: Column[DateTime] = column[DateTime]("last_updated_at")
/** Database column status DBType(int2) */
val status: Column[Short] = column[Short]("status")
/** Index over (dataSourceId) (database name conversation_data_source_id_key) */
val index1 = index("conversation_data_source_id_key", dataSourceId)
}
/** Collection-like TableQuery object for table Conversation */
lazy val Conversation = new TableQuery(tag => new Conversation(tag))
1つのテーブルでこれくらい。