k47.cz
mastodon twitter RSS
bandcamp explorer

ScalaQuery

Před nějakou dobou jsem porovnával for expression ze Scaly a LINQ z C# a došel jsem k závěru, že for comprehension umí skoro všechno, co LINQ. Jednoduchému for conprehenshion zcela nepřekvapivě chybí konstrukce pro pohodlné provádění operací jako group by, order by a samozřejmě obdoba LINQ to SQL, což je kompletní ORM.

For comprehension je konstrukce pro kompozici monád, takže není principiálně nemožné, vytvořit a skládat takové monády, které budou vykonávat všechny potřebné operace.

ScalaQuery je knihovna pro typově bezpečný přístup k relační databázi, která šikovně využívá právě for comprehension a je plně srovnatelná s LINQ.


Rozdíly oproti LINQ to SQL:


(Většina následujících ukázek pochází z wiki projektu)

Definice tabulek

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)
}

Jednoduché selecty

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

Implicitní join se vytvoří zřetězením více generá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

Explicitní 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

Explicitní 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 Odersky se na Scala Exchange 2011 letmo zmínil, že knihovna pro přístup k databázím a externím datovým zdrojům podobná ScalaQuery se v rámci projektu slick chystá přímo do nějaké budoucí verze Scaly

PS2: Krátce po publikování článku jsem se dozvěděl o Scala Integrated Query – ScalaQuery + nějaké zajímavé funkce navíc. (via @ijuma)

píše k47, ascii@k47.cz