slick 通用查询工具(带分页)

1. 首先定义一个Model特质

在指定ID查询和删除时用,根据实际情况定义字段。

package slick.crud

import slick.lifted.Rep

/**
 * @author Rubin
 * @version v1 2020/8/26 12:50
 */
trait Model {
  val fdId: Rep[String]
}

2. 再建一个CRUD特质

package repos

import repos.table.Tables.profile.api._
import slick.jdbc.JdbcProfile
import slick.lifted.TableQuery

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future, Promise}

/**
 * Slick CRUD util
 *
 * @author Rubin
 * @version v1 2020/8/26 11:52
 */
trait CRUD[M <: Model, R] {

  val database: JdbcProfile#Backend#Database
  val table: AnyRef

  private def getTable: TableQuery[Table[R]] = table.asInstanceOf[TableQuery[Table[R]]]

  def insert(entity: R): Future[Unit] = database.run(DBIO.seq(getTable += entity))

  def delete(fdId: String): Future[Int] = database.run(getTable.filter(_.asInstanceOf[M].fdId === fdId).delete)

  def update(entity: R): Future[Int] = database.run(getTable.insertOrUpdate(entity))

  def findById(fdId: String): Future[R] = database.run(getTable.filter(_.asInstanceOf[M].fdId === fdId).result.head)
  
  def page(page: Page[R]): Future[Page[R]] = {
    val p = Promise[Page[R]]
    try {
      val data = database.run(getTable.drop(page.page).take(page.limit).result)
      val size = database.run(getTable.size.result)
      page.data = Await.result(data, Duration.Inf)
      page.totalCount = Await.result(size, Duration.Inf)
      page.setTotalPage()
    } catch {
      case _: Throwable => p.failure _
    }
    p.success(page)
    p.future
  }
  
}

3. 分页用的Page

setTotalPage 在查询到总数的时候需要显示的调用一下,因为查询是异步的,直接引用的话拿不到实际值。

package slick.crud

/**
 * @author Rubin
 * @version v1 2020/8/25 11:20
 */
class Page[T](page_ : Int = 1, limit_ : Int = 10) {
  val page: Int = page_
  val limit: Int = limit_
  var totalCount: Int = _
  var totalPage: Int = _
  var data: Seq[T] = _

  def setTotalPage(): Unit = {
    val count = totalCount / limit_
    if (totalCount % limit_ == 0) totalPage = count else totalPage = count + 1
  }

}

4. 使用

1. 修改 slick-codegen 生成的代码,将表对应的对象添加 Model特质。

class TestUser(_tableTag: Tag) extends profile.api.Table[TestUserRow](_tableTag, Some("test"), "test_user") with Model {

package slick.table

import slick.crud.Model
// AUTO-GENERATED Slick data model
/** Stand-alone Slick data model for immediate use */
object Tables extends {
  val profile = slick.jdbc.MySQLProfile
} with Tables

/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.) */
trait Tables {
  val profile: slick.jdbc.JdbcProfile
  import profile.api._
  import slick.model.ForeignKeyAction
  // NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.
  import slick.jdbc.{GetResult => GR}

  /** DDL for all tables. Call .create to execute. */
  lazy val schema: profile.SchemaDescription = TestUser.schema
  @deprecated("Use .schema instead of .ddl", "3.0")
  def ddl = schema

  /** Entity class storing rows of table TestUser
   *  @param fdid Database column fdId SqlType(VARCHAR), PrimaryKey, Length(64,true)
   *  @param fdname Database column fdName SqlType(VARCHAR), Length(255,true), Default(None) */
  case class TestUserRow(fdid: String, fdname: Option[String] = None)
  /** GetResult implicit for fetching TestUserRow objects using plain SQL queries */
  implicit def GetResultTestUserRow(implicit e0: GR[String], e1: GR[Option[String]]): GR[TestUserRow] = GR{
    prs => import prs._
    TestUserRow.tupled((<<[String], <<?[String]))
  }
  /** Table description of table test_user. Objects of this class serve as prototypes for rows in queries. */
    class TestUser(_tableTag: Tag) extends profile.api.Table[TestUserRow](_tableTag, Some("test"), "test_user") with Model {
    def * = (fdId, fdName) <> (TestUserRow.tupled, TestUserRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    def ? = ((Rep.Some(fdId), fdName)).shaped.<>({r=>import r._; _1.map(_=> TestUserRow.tupled((_1.get, _2)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

    /** Database column fdId SqlType(VARCHAR), PrimaryKey, Length(64,true) */
    override val fdId: Rep[String] = column[String]("fdId", O.PrimaryKey, O.Length(64,varying=true))
    /** Database column fdName SqlType(VARCHAR), Length(255,true), Default(None) */
    val fdName: Rep[Option[String]] = column[Option[String]]("fdName", O.Length(255,varying=true), O.Default(None))
  }
  /** Collection-like TableQuery object for table TestUser */
  lazy val TestUser = new TableQuery(tag => new TestUser(tag))
}

2. DAO with CRUD[M <: Model, R]

package dao

import javax.inject.Inject
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import repos.CRUD
import slick.jdbc.JdbcProfile

import repos.table.Tables._

import scala.concurrent.ExecutionContext

/**
 * @author Rubin
 * @version v1 2020/8/26 17:38
 */
class TestUserDAO  @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
                            (implicit executionContext: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] with CRUD[TestUser, TestUserRow] {

  override val database = db
  override val table = TestUser

  // 其他业务查询...
  
}

3. 代码调用


for (i <- 1 to 500) {
  dao1.insert(TestUserRow("id_" + i))
}
  
dao1.delete("id_1").onComplete {
  case Success(value) => println(s"delete => $value")
}

dao1.findById("id_107").onComplete {
  case Success(value) => println(value.toArray.mkString("Array(", ", ", ")"))
}

dao1.update(TestUserRow("id_107", Option("修改了"))).onComplete {
  case Success(value) => println(s"update => $value")
}

5. 效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
👌 分页就不演示了,如果不觉得麻烦的话完全可以封装一个 springboot-jap 那种自动匹配的效果。

项目地址:https://gitee.com/vzhougm/slick_crud

最后附上官网给出的第三方通用查询工具 active-slick,玩的愉快~ 😘


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!