# Default database configuration db.default.driver=org.h2.Driver db.default.url=jdbc:h2:mem:play
# Orders database db.orders.driver=org.h2.Driver db.orders.url=jdbc:h2:mem:orders # Customers database db.customers.driver=org.h2.Driver db.customers.url=jdbc:h2:mem:customers
val appDependencies = Seq( "mysql" % "mysql-connector -java" % "5.1.18" )
play.api.db 包提供了訪問配置數據源的方法:java
import play.api.db._ val ds = DB.getDatasource()
val connection = DB.getConnection()
DB.withConnection { conn => // do whatever you need with the connection }
DB.withTransaction { conn => // do whatever you need with the connection }
db.default.driver= com.mysql.jdbc.Driver db.default.url="jdbc:mysql://localhost/world" db.default.user=root db.default.password=secret
Play 有時候會提供預編譯SQL statement, 但並非想隱藏SQL的底層細節. Play 只想節約大段查詢的打字時間,你徹底能夠返回來使用純的SQL.mysql
import anorm._ DB.withConnection { implicit c => val result: Boolean = SQL("Select 1").execute() }
val result: Int = SQL("delete from City where id = 99").executeUpdate()
val sqlQuery = SQL( """ select * from Country c join CountryLanguage l on l.CountryCode = c.Code where c.code = 'FRA'; """ )
SQL( """ select * from Country c join CountryLanguage l on l.CountryCode = c.Code where c.code = {countryCode}; """ ).on("countryCode" -> "FRA")
// Create an SQL query val selectCountries = SQL("Select * from Country") // Transform the resulting Stream[Row] as a List[(String,String)] val countries = selectCountries().map(row => row[String]("code") -> row[String]("name") ).toList
// First retrieve the first row val firstRow = SQL("Select count(*) as c from Country").apply().head // Next get the content of the 'c' column as Long val countryCount = firstRow[Long]("c")
case class SmallCountry(name:String) case class BigCountry(name:String) case class France val countries = SQL("Select name,population from Country")().collect { case Row("France", _) => France() case Row(name:String, pop:Int) if(pop > 1000000) => BigCountry(name) case Row(name:String, _) => SmallCountry(name) }
注意,既然 collect(...) 會忽略未定義函數,那它就容許你的代碼安全的那些你不指望的行.web
SQL("Select name,indepYear from Country")().collect { case Row(name:String, Some(year:Int)) => name -> year }
SQL("Select name,indepYear from Country")().map { row => row[String]("name") -> row[Int]("indepYear") }
SQL("Select name,indepYear from Country")().map { row => row[String]("name") -> row[Option[Int]]("indepYear") }
對於parser API也是一樣的狀況,接下來會看到。
val rowParser = scalar[Long]
val rsParser = scalar[Long].single
val count: Long = SQL("select count(*) from Country").as(scalar[Long].single)
val populations:List[String~Int] = { SQL("select * from Country").as( str("name") ~ int("population") * ) }
val result:List[String~Int] = { SQL("select * from Country").as(get[String]("name")~get[Int]("population")*) }
那麼,關於String~Int類型呢?它一個 Anorm 類型,不能在你的數據訪問層外使用.
str("name") ~ int("population") map { case n~p => (n,p) }注意:咱們在這裏建立了一個 tuple (String,Int),但沒人能阻止你RowParser轉成其它的類型,例如自定義case class。
val result:List[(String,Int)] = { SQL("select * from Country").as( str("name") ~ int("population") map(flatten) * ) }
接下來,讓咱們建立一個更復雜的例子。怎樣建立下面的查詢, 使得能夠獲取國家名和全部的國所使用的語言記錄呢?
select c.name, l.language from Country c join CountryLanguage l on l.CountryCode = c.Code where c.code = 'FRA'
var p: ResultSetParser[List[(String,String)] = { str("name") ~ str("language") map(flatten) * }
如今咱們獲得如下類型的結果:
List( ("France", "Arabic"), ("France", "French"), ("France", "Italian"), ("France", "Portuguese"), ("France", "Spanish"), ("France", "Turkish") )
case class SpokenLanguages(country:String, languages:Seq[String]) languages.headOption.map { f => SpokenLanguages(f._1, languages.map(_._2)) }
最後,咱們獲得了下面這個適用的函數:
case class SpokenLanguages(country:String, languages:Seq[String]) def spokenLanguages(countryCode: String): Option[SpokenLanguages] = { val languages: List[(String, String)] = SQL( """ select c.name, l.language from Country c join CountryLanguage l on l.CountryCode = c.Code where c.code = {code}; """ ) .on("code" -> countryCode) .as(str("name") ~ str("language") map(flatten) *) languages.headOption.map { f => SpokenLanguages(f._1, languages.map(_._2)) } }
To continue, letʼs complicate our example to separate the official language from the others:
case class SpokenLanguages( country:String, officialLanguage: Option[String], otherLanguages:Seq[String] ) def spokenLanguages(countryCode: String): Option[SpokenLanguages] = { val languages: List[(String, String, Boolean)] = SQL( """ select * from Country c join CountryLanguage l on l.CountryCode = c.Code where c.code = {code}; """ ) .on("code" -> countryCode) .as { str("name") ~ str("language") ~ str("isOfficial") map { case n~l~"T" => (n,l,true) case n~l~"F" => (n,l,false) } * } languages.headOption.map { f => SpokenLanguages( f._1, languages.find(_._3).map(_._2), languages.filterNot(_._3).map(_._2) ) } }
若是你在world sample數據庫嘗試該例子,你將得到:
$ spokenLanguages("FRA") > Some( SpokenLanguages(France,Some(French),List( Arabic, Italian, Portuguese, Spanish, Turkish )) )
import play.api.db._ import play.api.Play.current import org.scalaquery.ql._ import org.scalaquery.ql.TypeMapper._ import org.scalaquery.ql.extended.{ExtendedTable => Table} import org.scalaquery.ql.extended.H2Driver.Implicit._ import org.scalaquery.session._ object Task extends Table[(Long, String, Date, Boolean)]("tasks") { lazy val database = Database.forDataSource(DB.getDataSource()) def id = column[Long]("id", O PrimaryKey, O AutoInc) def name = column[String]("name", O NotNull) def dueDate = column[Date]("due_date") def done = column[Boolean]("done") def * = id ~ name ~ dueDate ~ done def findAll = database.withSession { implicit db:Session => (for(t <- this) yield t.id ~ t.name).list } }
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play" db.default.jndiName=DefaultDS