iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Modern Web

就是個Go,我也可以啦!GOGO系列 第 27

2023鐵人賽Day 27 Go X 你應該知道的ORM實作難題 X GORM 建立model及CRUD

  • 分享至 

  • xImage
  •  

後端系統其實最核心的就是與資料庫進行互動,我在寫rails時有一個module就是負責與資料庫的互動- ActiveRecord,這個module最核心的就是把record包裝成實體物件,也可以說是映射到ruby的物件上,於是乎,我就可以很方便的處理這些物件,我很依賴這樣子的處理方式,但必須說,這前提都是建立在物件導向語言上

然而,要知道go並不是一個物件導向語言,Go 傾向於提供較少的特性,並強調簡單和效能。這些特性使得 Go 在某些方面(例如並發)表現出色,但在其他方面(例如物件關聯映射(ORM))可能不是那麼直觀或易用,像是ORM

  • 類型嚴格性:
    • 靜態語言通常具有嚴格的類型系統,這意味著在編譯時期就需要確定所有變量的數據類型
    • ORM 需要將數據庫表(通常是靈活的結構)映射到物件(通常是嚴格定義的類型)上,這在靜態語言中可能會增加額外的複雜性,因為開發者需要確保數據庫結構與物件模型之間的類型一致性
  • 資料庫抽象的困難
    • 嘗試通過 ORM 抽象不同的數據庫可能會丟失某些數據庫的特定功能
  • 物件身份和數據庫主鍵的映射
    • 靜態語言通常在編譯時期就需要確定類型和結構,這可能使得動態生成的數據庫主鍵和物件身份之間的映射變得複雜
  • 繼承和多型的映射
    • 靜態語言中的繼承和多型可能難以直接映射到一個正規化的數據庫模型中。
    • 物件模型中的“是一個”(is-a)關係可能需要通過一些特定的數據庫設計模式(例如,表繼承或多型關聯)來實現

故,再go的後端系統上選擇實作orm時,一定要考慮這些問題
但好在,現在有一個穩定的orm package,今天我們就要來介紹這個package

Gorm

gorm 是一個用於 Go 語言的 Object Relational Mapping (ORM) 庫,它封裝了 database/sql 的許多功能,並提供了一個更高級的接口來與數據庫進行交互
所以前幾天說到的database/sql的功能,我們會依賴gorm 來幫我們完成

db docking:

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

db open:

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }
}

接下來我們來看看基本用法吧

聲明模型(Declaring Models)

在 GORM中,當我們談到"模型",我們通常是指用來代表和操作數據庫表的 Go 結構體。這些結構體包含一些字段,這些字段的類型通常是:

  • 基本 Go 類型:例如 stringintfloat64 等,用於儲存常見的數據類型

  • 指針或別名:例如 *string*int,允許字段為 nil,在數據庫中表示為 NULL 值,別名就是你為現有的類型定義的一個新名字,例如 type Age int

  • 自定義類型:這些類型實現了 ScannerValuer 接口,允許你自定義如何將數據讀取到 Go 程序中(Scanner)和如何將 Go 程序中的數據保存到數據庫中(Valuer),這對於處理數據庫中的特殊類型(例如 JSON、ENUM 等)或執行自定義數據處理非常有用。

type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivatedAt  sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}

慣例(Conventions)

GORM 傾向於選擇慣例優於配置,默認情況下,GORM 使用 ID 作為主鍵,將結構名複數化並轉換為 snake_case 作為表名,使用 snake_case 作為列名,並使用 CreatedAt 和 UpdatedAt 來表示建立時間/更新時間

這是GORM的慣例,當然也可以打破慣例,可以多參考官方的說明書

連接資料庫

PostgreSQL

import (
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)

dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

CRUD

Create

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

result := db.Create(&user) // pass pointer of data to Create

user.ID             // returns inserted data's primary key
result.Error        // returns error
result.RowsAffected // returns inserted records count

Read

GORM 提供了 First、Take 和 Last 方法來實現這一點,並在查詢數據庫時添加了 LIMIT 1 條件。如果沒有找到記錄,它將返回 ErrRecordNotFound 錯誤
也提供where查詢

// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;


Update

db.First(&user)

user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;

Delete

// Email's ID is `10`
db.Delete(&email)
// DELETE from emails where id = 10;

// Delete with additional conditions
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

示例參考 gorm


上一篇
# 2023鐵人賽Day 26 go X database 航母的作戰指揮中心
下一篇
2023鐵人賽Day 28 Go X 要來杯琴酒嗎
系列文
就是個Go,我也可以啦!GOGO30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言