k47.cz  — každý den dokud se vám to nezačne líbit
foto Praha výběr povídky kultura
twitter FB RSS

ScalaQuery

23. 6. 2011 — k47 (CC by)

Před ně­ja­kou dobou jsem po­rov­ná­val for ex­pres­sion ze Scaly a LINQ z C# a došel jsem k závěru, že for com­pre­hension umí skoro všechno, co LINQ. Jed­no­du­chému for con­pre­hen­shion zcela ne­pře­kva­pivě chybí kon­strukce pro po­ho­dlné pro­vá­dění ope­rací jako group by, order by a sa­mo­zřejmě obdoba LINQ to SQL, což je kom­pletní ORM.

For com­pre­hension je kon­strukce pro kom­po­zici monád, takže není prin­ci­pi­álně ne­možné, vy­tvo­řit a sklá­dat takové monády, které budou vy­ko­ná­vat všechny po­třebné ope­race.

Sca­la­Query je knihovna pro typově bez­pečný pří­stup k re­lační da­ta­bázi, která ši­kovně vy­u­žívá právě for com­pre­hension a je plně srov­na­telná s LINQ.


Roz­díly oproti LINQ to SQL:


(Vět­šina ná­sle­du­jí­cích ukázek po­chází z wiki pro­jektu)

De­fi­nice ta­bu­lek

object Users extends Table[(Int, String, Option[String])]("users") {
  def id    = column[Int]           ("id", O NotNull)
  def first = column[String]        ("first", O Default "NFN", O DBType "varchar(64)")
  def last  = column[Option[String]]("last")
  def * = id ~ first ~ last
}

object Orders extends Table[(Int, Int, String, Boolean, Option[Boolean])]("orders") {
  def userID  = column[Int]("userID", O NotNull)
  def orderID = column[Int]("orderID", O NotNull)
  def product = column[String]("product")
  def shipped = column[Boolean]("shipped", O Default false, O NotNull)
  def rebate  = column[Option[Boolean]]("rebate", O Default Some(false))
  def * = userID ~ orderID ~ product ~ shipped ~ rebate

  def user = foreignKey("user_fk", userID, Users)(_.id)
}

Jed­no­du­ché se­lecty

for (u <- Users) yield u.*
SELECT * FROM users

for {
  u <- Users if u.first === "Stefan"
} yield u.id ~ u.last
SELECT id, last FROM users WHERE first = 'Stefan'

Count

(for (u <- Users) yield u.*).count            // SELECT count(*) FROM users

for (u <- Users) yield u.first.count         // SELECT count(first) FROM users

for (u <- Users) yield u.first.countDistinct // SELECT count(distinct first) FROM users

Im­pli­citní join se vy­tvoří zře­tě­ze­ním více ge­ne­rá­torů

for {
  u <- Users
  o <- Orders if o.userID is u.id
} yield u.first ~ o.orderID
SELECT u.first, u.orderID FROM users u, orders o WHERE u.id = o.userID

Ex­pli­citní join

for {
  Join(u, o) <- Users innerJoin Orders on (_.id is _.userID)
} yield u.first ~ o.orderID
SELECT u.first, u.orderID FROM users u JOIN orders o ON u.id = o.userID

Join přes cizí klíč

for {
  o <- Orders if o.shipped === true
  u <- o.user
} yield u.first ~ o.product

Ex­pli­citní outer join

for {
  Join(u, o) <- Users leftJoin Orders on (_.id is _.userID)
} yield u.first ~ o.orderID.?

Group by

for {
  o <- Orders
  u <- o.user
  _ <- Query groupBy u.id
} yield u.first.min.get ~ o.orderID.count

PS: Matin Ode­r­sky se na Scala Ex­change 2011 letmo zmínil, že knihovna pro pří­stup k da­ta­bá­zím a ex­ter­ním da­to­vým zdro­jům po­dobná Sca­la­Query se v rámci pro­jektu slick chystá přímo do nějaké bu­doucí verze Scaly

PS2: Krátce po pu­b­li­ko­vání článku jsem se do­zvě­děl o Scala In­te­gra­ted Query – Sca­la­Query + nějaké za­jí­mavé funkce navíc. (via @ijuma)

píše k47 & hosté, ascii@k47.cz