iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Software Development

Golang 工程師的 SwiftUI 之旅系列 第 17

Day17: 好,該定義你的 DB Schema 了 - Table Migration

  • 分享至 

  • xImage
  •  

要將資料儲存進資料庫,第一個步驟就是要定義存進資料庫的結構長怎樣。

建立 DB Schema


首先我們要為需要儲存的資料建立 Table

例如我們有個 Member 結構如下

import Foundation

enum Position {
    case Top, JG, Mid, ADC, Support
}

struct Member {
    let id: Int64
    let name: String
    let position: Position
}

我們先新建一個 SQL.swift 檔案,並加上 import SQLite
為了方便,本系列 SQLite 有關的程式碼都會先寫在這邊:

https://ithelp.ithome.com.tw/upload/images/20231002/201623838A852cJYx1.png

建立 Expression

Expression 可以讓我們更好的利用套件跟 SQLite 互動,首先我們要為這個 Table 的每個欄位建立 Expression,每個 Expression 也同時定義了該欄位的型別:

let id = Expression<Int64>("id")
let name = Expression<String>("name")
let position = Expression<Position>("position")

我們在 SQLite.swift 加上 Member 的三個欄位定義

https://ithelp.ithome.com.tw/upload/images/20231002/20162383CByO0MoAnO.png

畫面中左右分割畫面,可以點選紅圈處的按鈕叫出來

支援的型別可以透過官方文件查詢

https://ithelp.ithome.com.tw/upload/images/20231002/20162383SGCi948Mx6.png

建立 Migration

定義完 Expression 之後就可以來寫建立 Table 的函式了。

首先我們要定義 Table 名稱,在 SQLite.swift 加上:

let tableMember = Table("members")

接著新增建立 Table 的函式:

這段程式碼會噴錯,解法請看下個段落

func createMemberTable(_ db: Connection) throws {
    try db.run(tableMember.create(ifNotExists: true) { t in
        t.column(id, primaryKey: .autoincrement)
        t.column(name, unique: true)
        t.column(position)
    })
    
    try db.run(tableMember.createIndex(name, ifNotExists: true))
}

這邊傳入 SQLite 的連線 Connection,使用來對資料庫進行操作。

利用定義的 tableMember 調用 create 方法來建立 Tablet.column 建立每一個欄位。

也可以利用 createIndex 來建立 index

以上程式碼等於下面的 SQL

CREATE TABLE IF NOT EXISTS "members" (
    "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    "name" TEXT UNIQUE NOT NULL,
    "position" INTEGER NOT NULL,
)

CREATE INDEX IF NOT EXISTS "index_members_on_name" ON "members" ("name")

自訂支援型別

上方程式碼會噴一個錯誤,原因是 Position 這個結構 SQLite 的型別不支援

https://ithelp.ithome.com.tw/upload/images/20231002/201623835NAKZLpysQ.png

所以我們要將 Position 實作 Value 這個 Protocol,來讓 SQLite 可以辨認他:

先將 Member.swift 加上 import SQLite

import SQLite

Position 改為以 IntrawValue

enum Position: Int {
    case Top = 1, JG = 2, Mid = 3, ADC = 4, Support = 5
}

Position 實作 Value

extension Position: Value {
    typealias Datatype = Int

    static var declaredDatatype: String {
        Int.declaredDatatype
    }
    
    static func fromDatatypeValue(_ datatypeValue: Int) -> Position {
        return Position(rawValue: datatypeValue) ?? .Support
    }
    
    var datatypeValue: Int {
        return self.rawValue
    }
}
  • DatatypePosition 要用哪種 SQLite 支援的型別儲存進資料庫內
  • declaredDatatypeSQLite 支援型別的辨認字串,SQLite 支援的型別會有 declaredDatatype 這個 static 變數
  • fromDatatypeValueSQLite 儲存的型別轉換成 Position
  • datatypeValuePosition 轉換成 SQLite 儲存的型別

上方定義完後,SQLite 就可以把 Position 轉換成 Int 儲存進 SQLite 裡了。

createMemberTable 這個函式也不會噴錯了。

執行 Migration

為了說明方便,我們直接在 ContentView 建立一個 SQLite 連線變數,接著在 init 內建立連線,並執行 createMemberTable

import SwiftUI
import SQLite

struct ContentView: SwiftUI.View {
    @State private var db: Connection
    
    init() {
        self.db = try! Connection(.inMemory)
        do {
            try createMemberTable(db)
        } catch {
            print(error)
            return
        }
        print("member table created")
    }
    
    var body: some SwiftUI.View {
        Text("Hello")
    }
}

Connection(.inMemory) 是連線到一個 記憶體暫存資料庫,若是軟體關閉,資料庫就會消失

如果要讓 SQLite 永久儲存在硬碟內,就要用 Connection("database") 就能將資料庫永久儲存了

總結


本章介紹如何定義、創建 SQLite Table,並實作 Value Protocol 來讓 SQLite 支援自訂的型別。

Clean Architecture 章節會介紹在哪個時機點創建 Table

參考資料



上一篇
Day16: 後端工程師的好朋友 - SQLite
下一篇
Day18: 新增、刪除、更新資料 - CREATE, UPDATE, DELETE
系列文
Golang 工程師的 SwiftUI 之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言