昨天成功連線資料庫之後,有透過 DB Browser for SQLite 看到 tasks.db
檔案裡面的內容,也有透過程式新增了一筆測試資料存進資料庫裡面~ 😆
所以今天,我們要把之前「 新增、讀取、更新、刪除 」的功能與資料庫做連接,讓我們做這些動作的時候,與資料庫的內容同步!
如果忘記 GORM 怎麼與資料庫連接的話,可以參考 Day 17 的介紹! 🙌
還記得之前我們有在 taskRepo.go
(repository)檔案裡面放了一個這個暫存資料嗎?
因為當時還沒有資料庫,所以就先建立一個 slice 取代:
// 暫存資料
var tasks = []model.Task{
{Item: "鐵人賽文章", Status: false},
}
而現在要把所有 CRUD 的功能都跟資料庫做連接的話,就會更改到以下檔案:
taskRepo.go
的資料來源、微調 task.Service.go
和 apiHandler.go
main.go
為了避免在多個檔案都重複寫一樣的資料庫連線程式,所以就再把「 資料庫 」獨立出來。建立一個檔案 database/db.go
專門處理資料庫的事情!db.go
內容:
package database
import (
"log"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var DB *gorm.DB // 建立
func InitDB() {
var err error
// 確認 DB 是否已初始化
if DB != nil {
return
}
// 連線 SQLite DB
DB, err = gorm.Open(sqlite.Open("tasks.db?_journal_mode=WAL"), &gorm.Config{})
if err != nil {
log.Fatal("failed to connect database: ", err)
}
}
📌 為什麼要把原本的 sqlite.Open(“tasks.db”)
改成 sqlite.Open("tasks.db?_journal_mode=WAL")
呢?
是這樣的~ 因為我在啟動 server 之後,有收到這個錯誤訊息:
database is locked
這是因為資料庫目前被其他連線鎖住,所以無法寫入。在 SQLite 中,同一個時間只能有一個寫入,雖然可以同時多個讀取,但是當要寫入時,就會鎖住整個 DB。
→ 鎖住的原因就是:Go server 在運作,但也同時用 DB Browser 打開了 tasks.db
。
那解決方式就是:
所以就更改原本的設定,加上 journal_mode=WAL
改善鎖定問題:
db, err := gorm.Open(sqlite.Open("tasks.db?_journal_mode=WAL"), &gorm.Config{})
→ WAL (Write-Ahead Logging) 允許同時讀取和寫入,提高並發能力。
把原本建立在 main 檔案的 DB 連線程式都搬到 database/db.go
檔案裡之後,我們就可以直接 import database,把原本很長的程式調整成兩行即可!main.go
內容:
package main
import (
"app/apiHandler"
"app/middleware"
"app/database" // 引入資料庫
"app/model"
"github.com/gin-gonic/gin"
)
func main() {
database.InitDB() // 初始資料庫
database.DB.AutoMigrate(&model.Task{}) // 自動建表
r := gin.Default()
r.Use(middleware.ErrorHandler())
r.GET("/tasks", apiHandler.GetTasks)
r.POST("/tasks", apiHandler.AddTask)
r.PATCH("/tasks/:id", apiHandler.UpdateTask)
r.DELETE("/tasks/:id", apiHandler.DeleteTask)
r.Run(":8080")
}
最後是更改 repository 的檔案!還記得之前我們在 repository 有建立一個暫時儲存 task 的的 slice 嗎?
// 暫存資料
var tasks = []model.Task{
{Item: "鐵人賽文章", Status: false},
}
這是還沒有連接資料庫時的替代方案,所有的「 新增、讀取、更新、刪除 」動作,都是針對這裡面的資料~
但現在我們有資料庫了!所以這部分就不需要囉!取而代之的是改用 GORM 的方式(詳細用法可以參考 Day 17)來操作。taskRepo.go
內容:
package repository
import (
"app/model"
"app/database"
)
func GetAllTasks() ([]model.Task){
var tasks []model.Task
database.DB.Find(&tasks)
return tasks
}
func AddTask(new *model.Task) (model.Task, error){
result := database.DB.Create(&new)
return *new, result.Error
}
func UpdateTask(id int, input model.UpdateTask)(model.Task, bool){
var task model.Task
err := database.DB.First(&task, id).Error; // 找出資料庫中 id 相符的 task
if err != nil{
return model.Task{}, false
}
if input.Item != nil{
task.Item = *input.Item
}
if input.Status != nil{
task.Status = *input.Status
}
database.DB.Save(&task) //寫入資料庫
return task, true
}
func DeleteTask(id int) bool{
var task model.Task
err := database.DB.First(&task, id).Error;
if err != nil{
return false
}
result := database.DB.Delete(&task).Error //刪除該筆資料
if result != nil{
return false
}
return true
}
完成 CRUD 的資料庫操作之後,要記得測試看看過程有沒有問題~如果有的話再細微調整與 repository 連動的 service 和 apiHandler 檔案。
以上就是今天分享了!