当前位置: 首页 > 工具软件 > Scala Slick > 使用案例 >

scala slick基本使用教程

盛建德
2023-12-01

参考:
http://slick.lightbend.com/doc/3.2.1/introduction.html
http://www.cnblogs.com/tiger-xc/p/5898585.html
http://www.cnblogs.com/tiger-xc/p/5898585.html

slick是scala生态中的数据库连接组件,跟scala一样有良好的函数式编程特性,本文以mysql和sqlite为例简单列举了一些slick的常用操作,作为经常参考的代码片段,如果需要深入研究,请参考slick官网文档

引入依赖

在sbt配置文件中引入dependency,并引入对应头文件
(1)针对mysql

libraryDependencies ++= Seq(
    "com.typesafe.slick" %% "slick" % "3.2.1",
    "org.slf4j" % "slf4j-nop" % "1.6.4",
    "mysql" % "mysql-connector-java" % "5.1.38",
    "com.typesafe.slick" %% "slick-hikaricp" % "3.2.1"
)

其中包含:

  • mysql的连接器jdbc
  • slick核心组件

头文件

import slick.jdbc.MySQLProfile.api._
import slick.lifted.TableQuery

(2)针对sqlite

libraryDependencies ++= Seq(
    "com.typesafe.slick" %% "slick" % "3.2.1",
    "org.slf4j" % "slf4j-nop" % "1.6.4",
    "org.xerial" % "sqlite-jdbc" % "3.20.0",
    "com.typesafe.slick" %% "slick-hikaricp" % "3.2.1"
)

其中包含:

  • sqlite的连接器jdbc
  • slick核心组件

头文件

import slick.jdbc.SQLiteProfile.api._
import slick.lifted.TableQuery

配置数据库

slick支持多种方式配置,

方法1:配置url

在代码中直接写
(1)mysql

val db = Database.forURL(
    url = "jdbc:mysql://localhost:3306/db_example?useUnicode=true&characterEncoding=UTF-8&useSSL=false",
    driver = "com.mysql.jdbc.Driver",
    user = "springuser",
    password = "ThePassword")

mysql需要预先安装mysql服务并启动
(2)sqlite

val sqlite_db = Database.forURL(
    url = "jdbc:sqlite:D:/Programs/sqlite3/newDB.db",
    driver = "org.sqlite.JDBC"
)

方法2:配置config

首先需要在工程某个目录放置conf配置文件,按照固定固定格式
例如:根目录/etc/database.conf

db {
  mysql_db = {
    url = "jdbc:mysql://localhost:3306/db_example?useUnicode=true&characterEncoding=UTF-8&useSSL=false",
    driver = "com.mysql.jdbc.Driver",
    user = "springuser",
    password = "ThePassword")
    numThreads = 5
  }

  sqlite_db = {
      url = "jdbc:sqlite:test.db",
      driver = "org.sqlite.JDBC"
      numThreads = 5
  }
}

在这个配置文件中可以添加更多属性,比如连接池,最大连接数等

然后用typesafe的config类load进来

val file = new File("etc/database.conf")
val config = ConfigFactory.parseFile(file)

(1)mysql

val db = Database.forConfig("mysql_db", config.getConfig("db"))

(2)sqlite

val sqlite_db = Database.forConfig("sqlite_db", config.getConfig("db"))

sqlite因为是本地数据库,跟读写文件差不多,不需要安装sqlite,slick自动会创建sqlite的文件并进行读写,url中配置的是sqlite的db文件的路径(绝对路径or相对路径)

数据库访问

数据库访问对于所有种类的数据库都是一致的

(1)通过绑定的数据结构

需要绑定scala对象和数据库表格

// define table structure(we can define many tables bindings)
object SlickDB
{
    // table name: scala_model
    case class UserInfo(id: Long, name: String, age: Int)

    class SlickModelTable(tag: Tag) extends Table[UserInfo](tag, "scala_model")
    {
        // define column attribute
        def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
        def name = column[String]("name")
        def age = column[Int]("age")
        def * = (id, name, age) <> (UserInfo.tupled, UserInfo.unapply)
    }
    def slick_table = TableQuery[SlickModelTable]
}

或者

// define table structure(we can define many tables bindings)
object SlickDB
{
    // table name: scala_model
    case class UserInfo(id: Long, name: String, age: Int)

    class SlickModelTable(tag: Tag) extends Table[UserInfo](tag, name)
    {
        // define column attribute
        def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
        def name = column[String]("name")
        def age = column[Int]("age")
        def * = (id, name, age) <> (UserInfo.tupled, UserInfo.unapply)
    }

    class SlickModelSubTable(tag: Tag) extends SlickModelTable(tag, "scala_model")

    def slick_table = TableQuery[SlickModelSubTable]
}

注意,同一个表结构可以绑定到多张数据库的表格,可以用class去继承,然后再绑定

然后就可以进行常用的增删改查操作,例如:

// query by condition
val res2 = Await.result(db.run(slick_table.filter(_.age > 25).result), Duration.Inf)

需要注意的是,slick的数据库操作是异步的,返回的是Future对象,需要用Await.result或者使用回调来得到结果

(2)通过sql访问

slick也支持直接用sql语句访问,这样就不用定义额外的class了,例如:

// query sql
val res6 = Await.result(db.run(sql"""select * from scala_model where name = 'mary'""".as[(Long, String, Int)]), Duration.Inf)

需要注意的是,返回的结果,需要对应到自定义的tuple结构中,字段类型一致

示例代码

import org.slf4j.LoggerFactory
import slick.driver.MySQLDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global // support andThen
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.{Failure, Success}

// define table structure(we can define many tables bindings)
object SlickDB
{
    // table name: scala_model
    case class UserInfo(id: Long, name: String, age: Int)

    class SlickModelTable(tag: Tag) extends Table[UserInfo](tag, "scala_model")
    {
        // define column attribute
        def id = column[Long]("id", O.PrimaryKey, O.AutoInc) // make sure here is primary key and auto inc(return column needed)
        def name = column[String]("name")
        def age = column[Int]("age")
        def * = (id, name, age) <> (UserInfo.tupled, UserInfo.unapply)
    }
    def slick_table = TableQuery[SlickModelTable]
}

import SlickDB._

object SlickTest extends App
{
    // init logger
    val logger = LoggerFactory.getLogger(getClass.getSimpleName)
    logger.info("slick test end")

    // config database
    val db = Database.forURL(
        url = "jdbc:mysql://localhost:3306/db_example?useUnicode=true&characterEncoding=UTF-8&useSSL=false",
        driver = "com.mysql.jdbc.Driver",
        user = "springuser",
        password = "ThePassword")

    // ---- use function

    // query all
    // slick run returns a future, we can use andThen to get async response and use Await.result to get result
    // usage1
    val query_action = slick_table.result
    val res1 = db.run(query_action).andThen {
        case Success(_) => println("query success")
        case Failure(e) => println("query failed ", e.getMessage)
    }

    // usage2
    db.run(slick_table.result).map {
        result => println(result)
    }

    // block thread to get select result
    Await.result(res1, 10 seconds) // specify the timeout


    // query by condition
    val res2 = Await.result(db.run(slick_table.filter(_.age > 25).result), Duration.Inf)

    // add(only 1 record)
    // val user1 = UserInfo(6L, "scarllet", 19)
    // val res3 = Await.result(db.run(slick_table += user1), Duration.Inf) // return the insert numbers: 1, so no need to return

    // add(batch records)
    val user1 = UserInfo(6L, "scarllet", 19)
    val user2 = UserInfo(7L, "mary", 21)
    val newArray = Seq[UserInfo](user1, user2)
    val res3 = Await.result(db.run(slick_table ++= newArray), Duration.Inf) // return the insert numbers: 2, so no need to return

    // update
    val new_user = UserInfo(3L, "tashaxing", 23)
    val res4 = Await.result(db.run(slick_table.filter(_.id === new_user.id).update(new_user)), Duration.Inf) // return effected row numbers

    // delete
    val res5 = Await.result(db.run(slick_table.filter(_.name === "lucy").delete), Duration.Inf)

    // return main column after insert
    val user = UserInfo(0, "ethan", 21)
    val save_sql = (slick_table returning slick_table.map(_.id)) += user
    val user_id = Await.result(db.run(save_sql), Duration.Inf) // return created id

    // ---- use sql

    // query sql
    val res6 = Await.result(db.run(sql"""select * from scala_model where name = 'mary'""".as[(Long, String, Int)]), Duration.Inf)

    // insert sql
    val id = 10L
    val name = "wilson"
    val age = 29
    val res7 = Await.result(db.run(sqlu"""insert into scala_model values($id, $name, $age)"""), Duration.Inf) // use variable outside
//    val res7 = Await.result(db.run(sqlu"""insert into scala_model values(10, 'bob', 28)"""), Duration.Inf) // use variable in string

    // update sql
    val res8 = Await.result(db.run(sqlu"""update scala_model set name='lily' where id=4"""), Duration.Inf)

    // delete sql
    val res9 = Await.result(db.run(sqlu"""delete from scala_model where name='mary'"""), Duration.Inf)


    logger.debug("slick test end")
}
 类似资料: