golang之mysql操作-GORM

GORM中文文檔
讀者可以直接看文檔,本文只是從文檔記錄個人常用的一些操作滤奈。

1.安裝

go get -u github.com/jinzhu/gorm

2.使用

官方一個快速入門的例子

package main

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type Product struct {
    gorm.Model //嵌入常用字段
    Code string
    Price uint
}

func main() {
    db, err := gorm.Open("mysql", "root:@(localhost:3306)/golang_mysql")
    if err != nil {
        panic("failed to connect database")
    }
    //關(guān)閉數(shù)據(jù)庫連接
    defer db.Close()

    //創(chuàng)建表
    db.AutoMigrate(&Product{})

    // 插入
    db.Create(&Product{Code: "L1212", Price: 1000})

    // 讀取
    var product Product
    db.First(&product, 1) // 查詢id為1的product
    db.First(&product, "code = ?", "L1212") // 查詢code為l1212的product

    // 更新
    db.Model(&product).Update("Price", 2000)

    // 刪除
    db.Delete(&product)
}

2.1 增

  • demo
package main

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type Product struct {
    gorm.Model //嵌入常用字段
    Code string
    Price uint
}

func main() {
    db, err := gorm.Open("mysql", "root:@(localhost:3306)/golang_mysql")
    if err != nil {
        panic("failed to connect database")
    }
    //關(guān)閉數(shù)據(jù)庫連接
    defer db.Close()

    //創(chuàng)建表
    db.AutoMigrate(&Product{})

    product := Product{Code:"No.001", Price:1000}
    fmt.Println(db.NewRecord(product)) //output: true
    db.Create(&product)
    fmt.Println(db.NewRecord(product))//output: false
}

db.NewRecord(product)用于檢查主鍵是否為空

  • 你可以通過 tag 定義字段的默認(rèn)值卓箫,比如:
type Animal struct {
    ID   int64
    Name string `gorm:"default:'galeone'"`
    Age  int64
}

生成的 SQL 語句會排除沒有值或值為 零值 的字段泻骤。 將記錄插入到數(shù)據(jù)庫后围肥,Gorm會從數(shù)據(jù)庫加載那些字段的值罩扇。注意 所有字段的零值, 比如 0, '', false 或者其它 零值婆芦,都不會保存到數(shù)據(jù)庫內(nèi)怕磨,但會使用他們的默認(rèn)值。

2.2 刪

O肌3辍!警告: 刪除記錄時或粮,請確保主鍵字段有值导饲,GORM 會通過主鍵去刪除記錄,如果主鍵為空氯材,GORM 會刪除該 model 的所有記錄渣锦。

// 刪除現(xiàn)有記錄
db.Delete(&email)
//// DELETE from emails where id=10;

// 為刪除 SQL 添加額外的 SQL 操作
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
  • 批量刪除
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
//// DELETE from emails where email LIKE "%jinzhu%";

db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
//// DELETE from emails where email LIKE "%jinzhu%";
  • 軟刪除
    如果一個 model 有 DeletedAt 字段,他將自動獲得軟刪除的功能氢哮! 當(dāng)調(diào)用 Delete 方法時袋毙, 記錄不會真正的從數(shù)據(jù)庫中被刪除, 只會將DeletedAt 字段的值會被設(shè)置為當(dāng)前時間
db.Delete(&user)
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// 批量刪除
db.Where("age = ?", 20).Delete(&User{})
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// 查詢記錄時會忽略被軟刪除的記錄
db.Where("age = 20").Find(&user)
//// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

// Unscoped 方法可以查詢被軟刪除的記錄
db.Unscoped().Where("age = 20").Find(&users)
//// SELECT * FROM users WHERE age = 20;
  • 物理刪除
// Unscoped 方法可以物理刪除記錄
db.Unscoped().Delete(&order)
//// DELETE FROM orders WHERE id=10;

2.3 查

//通過主鍵查詢第一條記錄
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;

// 拿到所有的記錄
db.Find(&users)
//// SELECT * FROM users;

// 查詢指定的某條記錄(只可在主鍵為整數(shù)型時使用)
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;
  • Where條件
  1. 普通sql
// 獲取第一條匹配的記錄
db.Where("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;

// 獲取全部匹配的記錄
db.Where("name = ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu';

// <>
db.Where("name <> ?", "jinzhu").Find(&users)

// IN
db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)

// 時間
db.Where("updated_at > ?", lastWeek).Find(&users)

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

2.AND (Struct & Map)

// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;

// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

// 主鍵的切片
db.Where([]int64{20, 21, 22}).Find(&users)
//// SELECT * FROM users WHERE id IN (20, 21, 22);

3.Not 條件

db.Not("name", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;

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

// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
//// SELECT * FROM users WHERE id NOT IN (1,2,3);

db.Not([]int64{}).First(&user)
//// SELECT * FROM users;

// Plain SQL
db.Not("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE NOT(name = "jinzhu");

// Struct
db.Not(User{Name: "jinzhu"}).First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu";

4.Or 條件

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
  • 高級查詢
    1.SubQuery 子查詢
db.Where("amount > ?", DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr()).Find(&orders)
// SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND
// (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));

2.選擇字段

db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users)
//// SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows()
//// SELECT COALESCE(age,'42') FROM users;

3.排序

db.Order("age desc, name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

// 多字段排序
db.Order("age desc").Order("name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

// 覆蓋排序
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
//// SELECT * FROM users ORDER BY age desc; (users1)
//// SELECT * FROM users ORDER BY age; (users2)

4.數(shù)量

db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;

// -1 取消 Limit 條件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
//// SELECT * FROM users LIMIT 10; (users1)
//// SELECT * FROM users; (users2)

5.偏移

db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;

// -1 取消 Offset 條件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
//// SELECT * FROM users OFFSET 10; (users1)
//// SELECT * FROM users; (users2)

6.總數(shù)

db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
//// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)

db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
//// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)

db.Table("deleted_users").Count(&count)
//// SELECT count(*) FROM deleted_users;

7.Group & Having

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
    ...
}
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
    ...
}
type Result struct {
    Date  time.Time
    Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

7.連接

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
    ...
}

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

// 多連接及參數(shù)
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

8.Pluck
Pluck冗尤,查詢 model 中的一個列作為切片听盖,如果您想要查詢多個列,您應(yīng)該使用 Scan

var ages []int64
db.Find(&users).Pluck("age", &ages)

var names []string
db.Model(&User{}).Pluck("name", &names)

db.Table("deleted_users").Pluck("name", &names)

// 想查詢多個字段生闲? 這樣做:
db.Select("name, age").Find(&users)

9.掃描
Scan媳溺,掃描結(jié)果至一個 struct.

type Result struct {
    Name string
    Age  int
}

var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)

// 原生SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)

2.4 改

  • 更新所有字段
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;
  • 更新修改字段
// 更新單個屬性,如果它有變化
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

// 根據(jù)給定的條件更新單個屬性
db.Model(&user).Where("active = ?", true).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

// 使用 map 更新多個屬性碍讯,只會更新其中有變化的屬性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
//// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 使用 struct 更新多個屬性,只會更新其中有變化且為非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

// 警告:當(dāng)使用 struct 更新時扯躺,GORM只會更新那些非零值的字段
// 對于下面的操作捉兴,不會發(fā)生任何更新,"", 0, false 都是其類型的零值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})

3.事務(wù)

GORM 默認(rèn)會將單個的 create, update, delete操作封裝在事務(wù)內(nèi)進行處理录语,以確保數(shù)據(jù)的完整性倍啥。
如果你想把多個 create, update, delete 操作作為一個原子操作,Transaction 就是用來完成這個的澎埠。

// 開啟事務(wù)
tx := db.Begin()

// 在事務(wù)中執(zhí)行具體的數(shù)據(jù)庫操作 (事務(wù)內(nèi)的操作使用 'tx' 執(zhí)行虽缕,而不是 'db')
tx.Create(...)

// ...

// 如果發(fā)生錯誤則執(zhí)行回滾
tx.Rollback()

// 或者(未發(fā)生錯誤時)提交事務(wù)
tx.Commit()

一個例子

func CreateAnimals(db *gorm.DB) error {
  // 注意,當(dāng)你在一個事務(wù)中應(yīng)使用 tx 作為數(shù)據(jù)庫句柄
  tx := db.Begin()
  defer func() {
    if r := recover(); r != nil {
      tx.Rollback()
    }
  }()

  if err := tx.Error; err != nil {
    return err
  }

  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  return tx.Commit().Error
}

4.原生sql

執(zhí)行原生 SQL 時蒲稳,不支持與其它方法的鏈?zhǔn)讲僮鳎?/p>

db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now(), []int64{11,22,33})

// Scan
type Result struct {
    Name string
    Age  int
}

var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
  • sql.Row 和 sql.Rows
    通過 *sql.Row 或 *sql.Rows 獲取查詢結(jié)果
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
    ...
    rows.Scan(&name, &age, &email)
    ...
}

// 原生 SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
    ...
    rows.Scan(&name, &age, &email)
    ...
}
  • 將 sql.Rows 掃描至 model
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()

for rows.Next() {
  var user User
  // ScanRows 掃描一行記錄到 user
  db.ScanRows(rows, &user)

  // do something
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氮趋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子江耀,更是在濱河造成了極大的恐慌剩胁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祥国,死亡現(xiàn)場離奇詭異昵观,居然都是意外死亡晾腔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門啊犬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灼擂,“玉大人,你說我怎么就攤上這事觉至√抻Γ” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵康谆,是天一觀的道長领斥。 經(jīng)常有香客問我,道長沃暗,這世上最難降的妖魔是什么月洛? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮孽锥,結(jié)果婚禮上嚼黔,老公的妹妹穿的比我還像新娘。我一直安慰自己惜辑,他們只是感情好唬涧,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盛撑,像睡著了一般碎节。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抵卫,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天狮荔,我揣著相機與錄音,去河邊找鬼介粘。 笑死殖氏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姻采。 我是一名探鬼主播雅采,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慨亲!你這毒婦竟也來了婚瓜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤巡雨,失蹤者是張志新(化名)和其女友劉穎闰渔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐望,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡冈涧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年茂附,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片督弓。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡营曼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出愚隧,到底是詐尸還是另有隱情蒂阱,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布狂塘,位于F島的核電站录煤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荞胡。R本人自食惡果不足惜妈踊,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泪漂。 院中可真熱鬧廊营,春花似錦、人聲如沸萝勤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敌卓。三九已至慎式,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趟径,已是汗流浹背瞬捕。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舵抹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓劣砍,卻偏偏與公主長得像惧蛹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刑枝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 回顧 字段類型(列類型):數(shù)值型香嗓,時間日期型和字符串類型 數(shù)值型:整型和小數(shù)型(浮點型和定點型) 時間日期型:da...
    翊溪閱讀 942評論 0 0
  • .數(shù)據(jù)庫 數(shù)據(jù)庫的發(fā)展: 文件系統(tǒng)(使用磁盤文件來存儲數(shù)據(jù))=>第一代數(shù)據(jù)庫(出現(xiàn)了網(wǎng)狀模型,層次模型的數(shù)據(jù)庫)=...
    小Q逛逛閱讀 964評論 0 2
  • 1. 了解SQL 1.1 數(shù)據(jù)庫基礎(chǔ) ? 學(xué)習(xí)到目前這個階段,我們就需要以某種方式與數(shù)據(jù)庫打交道装畅。在深入學(xué)習(xí)MyS...
    鋒享前端閱讀 1,066評論 0 1
  • Django 1.8.2 文檔Home | Table of contents | Index | Modules...
    軒轅小愛閱讀 2,348評論 0 2
  • 數(shù)據(jù)表是數(shù)據(jù)庫中最重要靠娱、最基本的操作對象,是數(shù)據(jù)存儲的基本單位掠兄。數(shù)據(jù)表被定義為列的集合像云,數(shù)據(jù)在表中是按照行和列的格...
    假正經(jīng)乄閱讀 1,902評論 0 2