数据库 Schema
我们之前 Slick 编程(2): 准备开发环境使用自动代码生成工具生成数据库表的 Slick 定义(使用 Lifted Embedding API ),本篇介绍如何手工来写这些 Schema 定义。
数据库表 Tables
为了能够使用 Slick 的 Lifted Embedding API 定义类型安全的查询,首先我们需要定义数据库表代表表中每行数据的类和对应于数据库表的 Schema 的 TableQuery 值,我们先看看自动生成的 Album 表个相关定义:
case class AlbumRow(albumid: Int, title: String, artistid: Int)
implicit def GetResultAlbumRow(implicit e0: GR[Int], e1: GR[String]): GR[AlbumRow] = GR{
prs => import prs._
AlbumRow.tupled((<<[Int], <<[String], <<[Int]))
}
class Album(tag: Tag) extends Table[AlbumRow](tag, "Album") {
def * = (albumid, title, artistid) <> (AlbumRow.tupled, AlbumRow.unapply)
def ? = (albumid.?, title.?, artistid.?).shaped.<>(
{r=>import r._; _1.map(_=> AlbumRow.tupled((_1.get, _2.get, _3.get)))},
(_:Any) => throw new Exception("Inserting into ? projection not supported."))
val albumid: Column[Int] = column[Int]("AlbumId", O.PrimaryKey)
val title: Column[String] = column[String]("Title")
val artistid: Column[Int] = column[Int]("ArtistId")
lazy val artistFk = foreignKey("FK_AlbumArtistId", artistid, Artist)
(r => r.artistid, onUpdate=ForeignKeyAction.NoAction, onDelete=ForeignKeyAction.NoAction)
}
lazy val Album = new TableQuery(tag => new Album(tag))
所有的字段(Column)使用 column 方法来定义,每个字段对应一个 Scala 类型和一个字段名称(对应到数据库表的定义),下面为 Slick 支持的基本数据类型:
- Numeric types: Byte, Short, Int, Long, BigDecimal, Float, Double
- LOB types: java.sql.Blob, java.sql.Clob, Array[Byte]
- Date types: java.sql.Date, java.sql.Time, java.sql.Timestamp
- Boolean
- String
- Unit
- java.util.UUID
支持 Null 的字段使用 Option[T] 来表示,其中 T 为上述基本数据类型,在字段名称之后,你可以使用一些可选的字段定义,这些可选定义定义在 table 的 O 对象中。下面为常用的定义
PrimaryKey 表明该字段为主键Default[T](defaultValue: T) 该字段缺省值DBType(dbType: String) 非标准字段类型,比如 DBType(“VARCHAR(20)”) 做为 String 类型AutoInc 自动增一的字段NotNull,Nullable 表明该字段是否可以为空
每个表定义都需要一个""方法定义了缺省映射,这定义了执行查询返回表格一行时的数据类型,Slick 的””不要求和数据库表的定义一一映射,你可以添加字段(复合字段)或者省略掉某个字段。
匹配过的表定义
可以使用自定义的数据类型做为”*”的映射,这可以使用双向映射操作符”“来完成。
比如:
def * = (albumid, title, artistid) <> (AlbumRow.tupled, AlbumRow.unapply)
约束
外键约束可以使用foreignKey来定义
lazy val artistFk = foreignKey("FK_AlbumArtistId", artistid, Artist)
(r => r.artistid, onUpdate=ForeignKeyAction.NoAction, onDelete=ForeignKeyAction.NoAction)
它的参数为外键约束的名称,本表字段名称,外键所在表名称,和一个函数,这个函数定义了外键约束,以及更新和删除外键时的行为)
主键约束可以使用 primaryKey 来定义,这主要用作定义复合主键的情况
val pk = primaryKey("PlaylistTrack_PK", (playlistid, trackid))
其它比如索引的情况和主键约束非常类似,比如:
class A(tag: Tag) extends Table[(Int, Int)](tag, "a") {
def k1 = column[Int]("k1")
def k2 = column[Int]("k2")
def * = (k1, k2)
def idx = index("idx_a", (k1, k2), unique = true)
// compiles to SQL:
// create unique index "idx_a" on "a" ("k1","k2")
}
数据库定义语言 DDL
数据库定义语句可以使用 TableQuery 的 ddl 方法,多个 DDL 对象可以使用 ++ 连接,比如:
val ddl = coffees.ddl ++ suppliers.ddl
db withDynSession {
ddl.create
ddl.drop
}
ddl.create 和 ddl.drop 可以创建表和删除表,如果需要看看对应的 SQL 语句,可以使用
val ddl = Album.ddl
ddl.createStatements.foreach(println)
ddl.dropStatements.foreach(println)
对应的 MySQL 语句为
create table `Album` (`AlbumId` INTEGER NOT NULL PRIMARY KEY,`Title` VARCHAR(254) NOT NULL,`ArtistId` INTEGER NOT NULL)
alter table `Album` add constraint `FK_AlbumArtistId` foreign key(`ArtistId`) references `Artist`(`ArtistId`) on update NO ACTION on delete NO ACTION
ALTER TABLE Album DROP FOREIGN KEY FK_AlbumArtistId
drop table `Album`
请发表评论