first try with slick

package

初期找到的书或者官网教程基本都是基于H2的,所以小白我开始Hello World路线

"com.typesafe.slick" %% "slick" % "3.2.0",
"com.h2database" % "h2" % "1.4.194",
"org.slf4j" % "slf4j-nop" % "1.6.4",
"com.typesafe.slick" %% "slick-hikaricp" % "3.2.0"

Since we are using H2 as our database system, we need to import features from Slick’s H2Profile. A profile’s api object contains all commonly needed imports from the profile and other parts of Slick such as database handling.

建立连接

通过配置文件

val db = Database.forConfig("h2mem1")
h2mem1 = {
url = "jdbc:h2:mem:test1"
driver = org.h2.Driver
connectionPool = disabled
keepAliveConnection = true
}

创建Schema

// Definition of the SUPPLIERS table

class Suppliers(tag: Tag) extends Table[(Int, String, String, String, String, String)](tag, "SUPPLIERS") {
def id = column[Int]("SUP_ID", O.PrimaryKey)

// This is the primary key column
def name = column[String]("SUP_NAME")

def street = column[String]("STREET")

def city = column[String]("CITY")

def state = column[String]("STATE")

def zip = column[String]("ZIP")

// Every table needs a * projection with the same type as the table's type parameter
def * = (id, name, street, city, state, zip)
}

val suppliers = TableQuery[Suppliers]

// Definition of the COFFEES table
class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
def name = column[String]("COF_NAME", O.PrimaryKey)

def supID = column[Int]("SUP_ID")

def price = column[Double]("PRICE")

def sales = column[Int]("SALES")

def total = column[Int]("TOTAL")

def * = (name, supID, price, sales, total)

// A reified foreign key relation that can be navigated to create a join
def supplier = foreignKey("SUP_FK", supID, suppliers)(_.id)
}

关键词

/** A Tag marks a specific row represented by an AbstractTable instance. */
sealed trait Tag {
/** Return a new instance of the AbstractTable carrying this Tag, with a new path */
def taggedAs(path: Node): AbstractTable[_]
}

Tag从注释上理解,可以看做是Table中每一行的抽象,或者叫类型
class确实是一个好的data model 的抽象容器,每次new一个就是一行

/** Represents a database table. Profiles add extension methods to TableQuery
* for operations that can be performed on tables but not on arbitrary
* queries, e.g. getting the table DDL. */
class TableQuery[E <: AbstractTable[_]](cons: Tag => E) extends Query[E, E#TableElementType, Seq] {
lazy val shaped = {
val baseTable = cons(new BaseTag { base =>
def taggedAs(path: Node): AbstractTable[_] = cons(new RefTag(path) {
def taggedAs(path: Node) = base.taggedAs(path)
})
})
ShapedValue(baseTable, RepShape[FlatShapeLevel, E, E#TableElementType])
}

lazy val toNode = shaped.toNode

/** Get the "raw" table row that represents the table itself, as opposed to
* a Path for a variable of the table's type. This method should generally
* not be called from user code. */
def baseTableRow: E = shaped.value
}

object TableQuery {
/** Create a TableQuery for a table row class using an arbitrary constructor function. */
def apply[E <: AbstractTable[_]](cons: Tag => E): TableQuery[E] =
new TableQuery[E](cons)

/** Create a TableQuery for a table row class which has a constructor of type (Tag). */
def apply[E <: AbstractTable[_]]: TableQuery[E] =
macro TableQueryMacroImpl.apply[E]
}

TableQuery 就是一个table的集合,从注释上看,是可以扩展对表操作的方法的

操作实例

def initialTables = {
val setup = DBIO.seq(
// Create the tables, including primary and foreign keys
(suppliers.schema ++ coffees.schema).create,

// Insert some suppliers
suppliers +=(101, "Acme, Inc.", "99 Market Street", "Groundsville", "CA", "95199"),
suppliers +=(49, "Superior Coffee", "1 Party Place", "Mendocino", "CA", "95460"),
suppliers +=(150, "The High Ground", "100 Coffee Lane", "Meadows", "CA", "93966"),
// Equivalent SQL code:
// insert into SUPPLIERS(SUP_ID, SUP_NAME, STREET, CITY, STATE, ZIP) values (?,?,?,?,?,?)

// Insert some coffees (using JDBC's batch insert feature, if supported by the DB)
coffees ++= Seq(
("Colombian", 101, 7.99, 0, 0),
("French_Roast", 49, 8.99, 0, 0),
("Espresso", 150, 9.99, 0, 0),
("Colombian_Decaf", 101, 8.99, 0, 0),
("French_Roast_Decaf", 49, 9.99, 0, 0)
)
// Equivalent SQL code:
// insert into COFFEES(COF_NAME, SUP_ID, PRICE, SALES, TOTAL) values (?,?,?,?,?)
)
val setupFuture = db.run(setup)
// Read all coffees and print them to the console
setupFuture.onComplete {
case Success(_) =>
println("Coffees:")
db.run(coffees.result).map(_.foreach {
case (name, supID, price, sales, total) =>
println(" " + name + "\t" + supID + "\t" + price + "\t" + sales + "\t" + total)
})
case Failure(e) =>
print("fail")
print(e.getMessage)
}

// Equivalent SQL code:
// select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES
}

这里我定义了一个方法来操作H2
DBIO.seq里包含了一系列对DB的操作,用了slick看起来就想操作collection一样
db.run是异步的所以我们 需要Future来解决