gorm字典

gorm的使用

gorm的安裝

打開go運行的文件夾下的終端,創建main.go文件 執行下面的指令初始化mod

go mod init main.go

安裝gorm
官方給的示例是

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
但是我們這里要使用mysql數據庫 所以就對第二個依賴進行一部分修改

got get -u gorm.io/driver/mysql

gorm的使用 增刪改查

package main

import (
    "fmt"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type Product struct {
    gorm.Model
    Code  string
    Price uint
}

func main() {
    dsn := "root:1296729980@tcp(127.0.0.1:3306)/go_db"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        fmt.Println(err)
    }
    // //創建表
    db.AutoMigrate(&Product{})
    p := Product{
        Price: 200,
        Code:  "2",
    }
    //插入數據
    db.Create(&p)
    //查看數據
    var product Product
    // db.First(&product, 2) // 按照整形鏈查找
    db.First(&product, 1)
    fmt.Printf("product: %v\n", product)

    //改
    //這里有坑,修改單個字段使用的是update 修改多個使用到的是updates
    db.Model(&product).Update("Price", 300)
    //更新多個字段
    db.Model(&product).Updates(Product{Price:300,Code:"3"})
    db.Model(&product).Updates(map[string]interface{}{"Price": 300, "Code": "3"})
    //刪
    db.Delete(&product, 1)
}

gorm 模型 model

gorm的模型更多的是一種約定,而不是配置

gorm.Model是grom的一種結構體代表的是

type Model struct{
    ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

當我們生命gorm結構體時,如果不想直接寫gorm.Model的話,將上面的這些字段加入到我們聲明的結構體當中效果也是一樣的。

字段權限控制

當我們對字段CRUD時,默認字段是具有全部的權限的

gorm允許我們使用使用標簽來控制字段級的權限

type User struct {
  Name string `gorm:"<-:create"` // allow read and create
  Name string `gorm:"<-:update"` // allow read and update
  Name string `gorm:"<-"`        // allow read and write (create and update)
  Name string `gorm:"<-:false"`  // allow read, disable write permission
  Name string `gorm:"->"`        // readonly (disable write permission unless it configured)
  Name string `gorm:"->;<-:create"` // allow read and create
  Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
  Name string `gorm:"-"`            // ignore this field when write and read with struct
  Name string `gorm:"-:all"`        // ignore this field when write, read and migrate with struct
  Name string `gorm:"-:migration"`  // ignore this field when migrate with struct
}

當使用gorm Migrator創建表時,不會創建被忽略的字段名

創建、修改、刪除時間

gorm約定默認使用

    CreatedAt 
    UpdatedAt 
    DeletedAt

這三個字段名來分別記載創建時間,修改時間,刪除時間
如果不想使用這三個字段可以使用標簽進行更改

type User struct {
  CreatedAt time.Time // 在創建時,如果該字段值為零值,則使用當前時間填充
  UpdatedAt int       // 在創建時該字段值為零值或者在更新時,使用當前時間戳秒數填充
  Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用時間戳填納秒數充更新時間
  Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用時間戳毫秒數填充更新時間
  Created   int64 `gorm:"autoCreateTime"`      // 使用時間戳秒數填充創建時間
}

gorm 連接數據庫

gorm支持連接的數據庫有以下幾種MySQL, PostgreSQL, SQlite, SQL Server
我們主要介紹mysql
如果想要了解更多可以訪問官方文檔地址

官方鏈接mysql的約定格式為

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

func main() {
  // 參考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 獲取詳情
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

這只是個實例 我們在使用時dsn會這樣寫

    dsn := "root:1296729980@tcp(127.0.0.1:3306)/go_db"

root為名稱,root后跟的是數據庫密碼,go_db是我們在root下創建的數據庫

想要正確的處理 time.Time ,您需要帶上 parseTime 參數, (更多參數) 要支持完整的 UTF-8 編碼,您需要將 charset=utf8 更改為 charset=utf8mb4 查看 此文章 獲取詳情

如果想要使用更加精細的配置,官方也給了配置選項,以下表明的是默認配置選項

db, err := gorm.Open(mysql.New(mysql.Config{
  DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
  DefaultStringSize: 256, // string 類型字段的默認長度
  DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的數據庫不支持
  DontSupportRenameIndex: true, // 重命名索引時采用刪除并新建的方式,MySQL 5.7 之前的數據庫和 MariaDB 不支持重命名索引
  DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的數據庫和 MariaDB 不支持重命名列
  SkipInitializeWithVersion: false, // 根據當前 MySQL 版本自動配置
}), &gorm.Config{})

示例:

package main

import (
    "fmt"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func main() {
    dsn := "root:1296729980@tcp(localhost:3306)/go_db"
    d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(d)
}

&{0x140001da630 <nil> 0 0x140002aa000 1}

我們會得到一個地址,代表鏈接成功

創建記錄

創建一個記錄

package main

import (
    "fmt"
    "time"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

var db *gorm.DB

type User struct {
    gorm.Model
    Name     string
    Age      uint
    Birthday time.Time
}

func init() {
    dsn := "root:1296729980@tcp(localhost:3306)/go_db"
    d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        fmt.Println(err)
    }
    db = d
}
func InsertValue() {
    user := User{Name: "小明", Age: 11, Birthday: time.Now()}
    result := db.Create(&user)
    fmt.Println("userID", user.ID) // 顯示添加ID
    fmt.Println("Error", result.Error)  //錯誤信息
    fmt.Println("Rows", result.RowsAffected) //改變行數
}

func CreateTable() {
    db.AutoMigrate(&User{})
}

func main() {
    CreateTable()
    InsertValue()
}

??注意,在命名結構體時,我們所要用到的所有字段必須首字母大寫,這是一種約定,否則gorm將無法將字段名添加至數據庫中

按照字段傳值

func InsertValue() {
    user := User{Name: "小紅", Age: 12, Birthday: time.Now()}
    db.Select("Name","Age").Create(&user)
    db.Omit("Name", "Age").Create(&user)
}

db.Select(column1,column).Create()指只添加我們查詢到的字段名,其他的數據忽略
db.Omit(column1,column2).Create()指忽略我們傳的字段名,其他字段名全部添加

|  4 | 2022-06-15 06:20:56.537 | 2022-06-15 06:20:56.537 | NULL       | 小紅   |   12 | NULL                    |
|  5 | 2022-06-15 06:22:15.330 | 2022-06-15 06:22:15.330 | NULL       | NULL   | NULL | 2022-06-15 06:22:15.329 |

批量插入

批量插入就是傳一個slice給Create(),grom會單獨創建出一條sql語句,鉤子在這時依然可以進行使用

user := []User{{Name: "小剛", Age: 11}, {Name: "小強", Age: 10}}

鉤子

Hook 是在創建、查詢、更新、刪除等操作之前、之后調用的函數。

hook執行順序

// 開始事務
BeforeSave
BeforeCreate
// 關聯前的 save
// 插入記錄至 db
// 關聯后的 save
AfterCreate
AfterSave
// 提交或回滾事務

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  u.UUID = uuid.New()

  if !u.IsValid() {
    err = errors.New("can't save invalid data")
  }
  return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  if u.ID == 1 {
    tx.Model(u).Update("role", "admin")
  }
  return
}

所有對數據庫的操作和伴隨的hook是一個事務,如果有一項報錯,那么之前所有的操作都將被回滾

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    if u.ID == 1 {
        tx.Model(u).Update("role", "admin")
    }
    fmt.Println("AfterCreate")
    return
}

我們會發現在上傳多個數據的時候,會執行多次hook

查詢

檢索單個對象

// 獲取第一條記錄(主鍵升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 獲取一條記錄,沒有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 獲取最后一條記錄(主鍵降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)
result.RowsAffected // 返回找到的記錄數
result.Error        // returns error or nil

// 檢查 ErrRecordNotFound 錯誤
errors.Is(result.Error, gorm.ErrRecordNotFound)

檢索多條記錄

檢索多條記錄時一定注意,傳入的是一個切片的形式

    var user []User
    db.Find(&user)
    for _, u := range user {
        fmt.Printf("u: %v\n", u.ID)
    }

使用主鍵進行檢索

db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;

db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;

db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);

根據結構體查字段

    var user []User
    db.Where(&User{Name: "小剛"}).Find(&user)
    for _, u := range user {
        fmt.Printf("u: %v\n", u.ID)
    }

根據結構體查詢字段

var user User
    db.Select("name").First(&user)
    fmt.Printf("user: %v\n", user.Name)

通過這種方式我們可以檢索到name

智能選擇字段

新建一個結構體

type UserSelect struct {
    Name string
}

使用

    db.Model(&User{}).Limit(10).Find(&user)
    // SELECT `id`, `name` FROM `users` LIMIT 10
    fmt.Println(user)

我們可以得到表中每一行的name
同時gorm還支持子查詢,form子查詢以及group子查詢

更新

保存所有字段

user.Name = "小小"
    user.Age = 10
    user.Birthday = time.Now()
    db.Save(&user)

db.Save()會保存所有的字段,即使是0值也會被保存
執行上面的語句我們在查詢表的時候,可以發現表中多了一條名為小小的數據

更改一列

db.Select("name").Model(&user).Where("id=?", 15).Update("name", "小一")
    fmt.Printf("user.Name: %v\n", user.Name)

上述語句相當于sql語句

SELECT name from UPDATE users SET name='小一', updated_at='2022-06-15 09:45:39.965' WHERE id=15 AND active=true;

更改多列

使用struct更新屬性,只能更新非零值

db.Select("name").Model(&user).Where("age=?", 10).Updates(&User{Name: "小三"})

使用map[string]更新屬性

    db.Select("name").Model(&user).Where("age=?", 10).Updates(map[string]interface{}{"name":"小三"})

更新多列時,使用到的事updates而不再是update,并且需要傳一個結構體或者是map類型的數據

更新選定的字段

我們先定義一個結構體

    var user User
    user.Name = "new_name"
    user.Age = 11
    user.Birthday = time.Now()

有時我們不想把所有的字段全部都進行更改,我們可以使用到select或者是omit

    db.Model(&user).Select("name").Where("age=?", 10).Updates(&user)
    db.Model(&user).Omit("name").Where("age=?", 10).Updates(&user)

select是對所有age為10的數據的name全部進行更改,而omit則是除了name以外的其他字段進行更改

阻止全局更新

db.Model(&user).Select("name").Updates(&user)

像是這種不加任何判斷直接進行全局更新的情況,gorm一般是不會允許的,并拋出這樣的錯誤

WHERE conditions required

對此我們必須加一些條件或者使用sql原生語句

db.Exec("UPDATE users SET name = ?", "jinzhu")
// UPDATE users SET name = "jinzhu"

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu"

刪除

db.Where("name = ?", "小紅").Delete(&user)
// DELETE from emails where id = 10 AND name = "jinzhu";

直接刪除所有名字為小紅的數據

根據主鍵刪除

刪除主鍵為10的數據

db.Delete(&user,10)

刪除主鍵為1,2,

db.Delete(&users, []int{1,2,3})

阻止全局刪除

db.Exec("DELETE FROM users")
// DELETE FROM users

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
// DELETE FROM users

軟刪除

我們引入了gorm.Model后,因為gorm.deletedat字段的存在,其實數據并沒有被真正的刪除,而是在deleted_at中加入了一個時間戳

查找被軟刪除的記錄

db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

永久刪除
直接將數據從數據表中刪除

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 簡介 gorm[https://gorm.io/] 是 go orm 實現之一,這篇文章將以 mysql 為例,帶...
    PurelightMe閱讀 11,820評論 0 3
  • [TOC] 說明 請先閱讀官方文檔[http://gorm.book.jasperxu.com/],由于官方文檔對...
    小晚不晚閱讀 1,698評論 0 1
  • Gorm for SQL Join 關聯查詢 Belongs To: http://gorm.io/docs/be...
    堅果jimbowhy閱讀 3,797評論 0 0
  • 最近在想給這個小站增加點贊和評論功能,第三方系統又有各種限制,就想自己弄個后端,實現類似的功能,對于個人來說,數據...
    hatlonely閱讀 52,584評論 3 21
  • 寫在前面: GOrm版本: v1.9.11GORM是Go上的數據庫Lib,簡單易用,而且是國人開發的,中文文檔也很...
    沉寂之舟閱讀 6,225評論 0 3