LoginSignup
13
6

More than 5 years have passed since last update.

Slick3野郎の備忘録1

Last updated at Posted at 2016-11-18

Slick3野郎の備忘録1

ドキュメント類

SQLの実行方法あれやこれや

基本は

main.scala
do.run(<action>)

action部分

action.scala
//insert系
Users += User(3, "charles", "charles@example.com")
//update系
Users.filter { _.name.startsWith("alice") }.map(_.name).update("alice-updated")       
//delete系
Users.filter{ _.name === "alice" }.delete

group byとかこんな感じ 参考にしたサイト

groupby.sql
SELECT s.club_id, s.classroom, count(*)
  FROM Sutudents s
  GROUP BY s.club_id, u.classroom

↓slickで書くと

slick.scala
val q3 = for {
  ((ci, cr), ss) <- Students.groupBy(s => (s.clubId, s.classroom))
} yield (ci, cr, ss.length)

val r3: List[(Option[Long], String, Int)] = q3.list()

stackoverflow リレーション状態の結果を取得する方法

joinしてGroupByするケース ドキュメント少なッ!

main.scala

// 入れ物の型を用意します
// parents, childs は別途modelが存在する想定です
case class ChildSummaryEntity(childsCount: Int)
case class ParentEntity(id: Option[Long], parentname: String, summary: Option[ChildSummaryEntity])

def getNestedEntityByGroup(parentId: Long): Future[Seq[ParentEntity]] = {

    val sampleId: Long = 159
    val a = 
      // where句
      parents.filter( _.id === parentId ).flatMap(p =>
      childs.filter( _.parentId === p.id ).map((p, _ )))
      // gourp by節 ここで2つ指定するとselect句のPはTuple型になるので注意
      .groupBy (u => (p._1.id, p._1.parentname))

      // select句
      .map { case (p, c) => (
        p._1,
        p._2,
        c.map(_._1).length)} // select column
      .result

    db.run(a.transactionally).flatMap { r =>
      Future.successful {
        r.map {
          // select句の項目を元にネストオブジェクトを作成する
          case (i, n, s) => ParentEntity(i, n, Some(ChildSummaryEntity(s)) )
        }
      }
    }
  }

注意点

joinLeftなどで外部結合する場合、Left側はOption扱いになるので注意が必要。
LeftサイドのColumnを扱う場合はmap(.hoge)ではなくflatMap(.hoge)とする必要がある。
それはなぜか?
mapを使った場合、出力される生SQLを観察すると一目瞭然だが、射影に不要なcase文が混入する場合がある。

slick3におけるflatMapについての理解

判りやすいblog

生SQL

公式: slick-plainsql

サービストレイトに実装する場合を前提に進めます

  1. 利用部分のサンプルはこちら
route.scala
trait FuckServiceRoute  extends FuckService  {
// (_.toJson)でJSON変換する時のフォーマット用implict
implicit val fuckFormat = jsonFormat3(FuckEntity)
trait FuckRoute  extends FuckService {
  val fuckRoute = pathPrefix("fuck") {
    get {
      complete(getFuckByRawSQL.map(_.toJson))
    }
  }
}
  1. 実行部分のTraitはこちら
transfer.scala
import models.FuckTransfer
trait FuckService extends FuckTable with FuckTransfer {
  // マッピングはFuckTransfer.FuckEntity側でimplictで定義するのでここでは何も気にする必要なし
  def getFuckByRawSQL() : Future[Seq[FuckEntity]]={
    // 文字列補完をしてみるslick側でSQLインジェクション対応までやってくれます
    val s = "fuck"   
    val a = sql"SELECT id, $s, created_at FROM fuck".as[FuckEntity]
    db.run(a)
  }
}
  1. トランスファーのTraitはこちら
model.scala
package models
import slick.jdbc.GetResult
import org.joda.time.DateTime
// 素のslickには、jodatimeのimplictがないので追加、これがないとDateTimeは利用できないぜ
import com.github.tototoshi.slick.MySQLJodaSupport._
trait FuckTransfer {
  case class FuckEntity(userId: Long, fuck: String, createAt: DateTime)
  //これだけで勝手にマッピングしてくれますが、順番大事 名前大事
  implicit val getFuckEntityResult = GetResult(r => FuckEntity(r.<<, r.<<, r.<<))
}

トランザクション

公式ドキュメント

ロールバック stackoverflow 動作未確認

transactionnally.scala
  //トランザクション中で複数の処理を事項する場合
  def getAcidTest(): Future[Unit]  ={
    db.run(
      DBIO.seq(
        // みかんを蜜柑に変えて値段を変更(Item丸ごと更新)
        items.filter(_.id === 2).update(Item(2, "蜜柑", 250)),
        // 蜜柑をミカンに変更(nameだけを更新)
        items.filter(_.id === 2).map(_.name).update("ミカン"),
        // idが2より大きい物のpriceを800に(複数レコードをまとめて更新)
        items.filter(_.id > 2).map(_.price).update(800)
      ).transactionally
    )
  }

db.runで実行するactionは一つなので複数の処理が必要な場合はアクション合成する
※○!=trueなら処理するなど
1. qiita
2. stackoverflow

つまり複数の処理を行う場合は全てアクション合成する必要がある
検索結果をもとに計算処理をし、生SQLを発行して結果を合成する時も同じ(逆に面倒くさいわっ!)
stack overflow

アクション合成

公式ドキュメント

注意点

  1. ジェネリック系の雛型に出来るのは「case class」だけ
  2. slickのmodelはトレイト

Slick3野郎の備忘録2へつづく

13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6