鍍金池/ 教程/ Scala/ 數(shù)據(jù)庫 Schema
概述
基本查詢
查詢(二)
查詢(三)
直接使用 SQL 語句
數(shù)據(jù)庫連接和事務(wù)處理
數(shù)據(jù)庫 Schema
查詢(一)
準(zhǔn)備開發(fā)環(huán)境

數(shù)據(jù)庫 Schema

我們之前 Slick 編程(2): 準(zhǔn)備開發(fā)環(huán)境使用自動代碼生成工具生成數(shù)據(jù)庫表的 Slick 定義(使用 Lifted Embedding API ),本篇介紹如何手工來寫這些 Schema 定義。

數(shù)據(jù)庫表 Tables

為了能夠使用 Slick 的 Lifted Embedding API 定義類型安全的查詢,首先我們需要定義數(shù)據(jù)庫表代表表中每行數(shù)據(jù)的類和對應(yīng)于數(shù)據(jù)庫表的 Schema 的 TableQuery 值,我們先看看自動生成的 Album 表個相關(guān)定義:

/** Entity class storing rows of table Album
   *  @param albumid Database column AlbumId PrimaryKey
   *  @param title Database column Title 
   *  @param artistid Database column ArtistId  */
  case class AlbumRow(albumid: Int, title: String, artistid: Int)
  /** GetResult implicit for fetching AlbumRow objects using plain SQL queries */
  implicit def GetResultAlbumRow(implicit e0: GR[Int], e1: GR[String]): GR[AlbumRow] = GR{
    prs => import prs._
    AlbumRow.tupled((<<[Int], <<[String], <<[Int]))
  }
  /** Table description of table Album. Objects of this class serve as prototypes for rows in queries. */
  class Album(tag: Tag) extends Table[AlbumRow](tag, "Album") {
    def * = (albumid, title, artistid) <> (AlbumRow.tupled, AlbumRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    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."))

    /** Database column AlbumId PrimaryKey */
    val albumid: Column[Int] = column[Int]("AlbumId", O.PrimaryKey)
    /** Database column Title  */
    val title: Column[String] = column[String]("Title")
    /** Database column ArtistId  */
    val artistid: Column[Int] = column[Int]("ArtistId")

    /** Foreign key referencing Artist (database name FK_AlbumArtistId) */
    lazy val artistFk = foreignKey("FK_AlbumArtistId", artistid, Artist)
        (r => r.artistid, onUpdate=ForeignKeyAction.NoAction, onDelete=ForeignKeyAction.NoAction)
  }
  /** Collection-like TableQuery object for table Album */
  lazy val Album = new TableQuery(tag => new Album(tag))

所有的字段(Column)使用 column 方法來定義,每個字段對應(yīng)一個 Scala 類型和一個字段名稱(對應(yīng)到數(shù)據(jù)庫表的定義),下面為 Slick 支持的基本數(shù)據(jù)類型:

  • 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 為上述基本數(shù)據(jù)類型,在字段名稱之后,你可以使用一些可選的字段定義,這些可選定義定義在 table 的 O 對象中。下面為常用的定義

PrimaryKey 表明該字段為主鍵 Default[T](defaultValue: T) 該字段缺省值 DBType(dbType: String) 非標(biāo)準(zhǔn)字段類型,比如 DBType(“VARCHAR(20)”) 做為 String 類型 AutoInc 自動增一的字段 NotNull,Nullable 表明該字段是否可以為空

每個表定義都需要一個""方法定義了缺省映射,這定義了執(zhí)行查詢返回表格一行時(shí)的數(shù)據(jù)類型,Slick 的””不要求和數(shù)據(jù)庫表的定義一一映射,你可以添加字段(復(fù)合字段)或者省略掉某個字段。

匹配過的表定義

可以使用自定義的數(shù)據(jù)類型做為”*”的映射,這可以使用雙向映射操作符”<>“來完成。

比如:

def * = (albumid, title, artistid) <> (AlbumRow.tupled, AlbumRow.unapply)

約束

外鍵約束可以使用foreignKey來定義

/** Foreign key referencing Artist (database name FK_AlbumArtistId) */
    lazy val artistFk = foreignKey("FK_AlbumArtistId", artistid, Artist)
        (r => r.artistid, onUpdate=ForeignKeyAction.NoAction, onDelete=ForeignKeyAction.NoAction)

它的參數(shù)為外鍵約束的名稱,本表字段名稱,外鍵所在表名稱,和一個函數(shù),這個函數(shù)定義了外鍵約束,以及更新和刪除外鍵時(shí)的行為)

主鍵約束可以使用 primaryKey 來定義,這主要用作定義復(fù)合主鍵的情況

/** Primary key of Playlisttrack (database name PlaylistTrack_PK) */
    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")
}

數(shù)據(jù)庫定義語言 DDL

數(shù)據(jù)庫定義語句可以使用 TableQuery 的 ddl 方法,多個 DDL 對象可以使用 ++ 連接, 比如:

val ddl = coffees.ddl ++ suppliers.ddl
db withDynSession {
    ddl.create
    //...
    ddl.drop
}

ddl.create 和 ddl.drop 可以創(chuàng)建表和刪除表,如果需要看看對應(yīng)的 SQL 語句,可以使用

val ddl = Album.ddl
ddl.createStatements.foreach(println)
ddl.dropStatements.foreach(println)

對應(yīng)的 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`
下一篇:基本查詢