Gorm 操作入門

什么是 ORM

ORM(Object-Relational-Mapping) 是在關系型數據庫和對象之間做一個映射, 在操作數據庫時
就不需要和復雜的 sql 語句打交道, 而是像操作對象一樣操作sql就可以

GORMgo 語言的一個 orm 框架

# 使用 mysql 創建一個 users 表
create table users
(
    id       int(11),
    username varchar(255),
    age      int(3),
    sex      int
);
// 使用 gorm 對象創建一個 users 表
type User struct {
    ID int
    UserName string
    Age int
    Sex string
}

DB.CreateTable(&User{}) // 創建表

安裝

# 安裝 gorm
go get -u gorm.io/gorm 
# 安裝 mysql 驅動
go get gorm.io/driver/mysql

連接數據庫 mysql

var (
    DB  *gorm.DB
    err error
)

func init() {
    // 參考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 獲取詳情
    // 數據庫用戶名:密碼@tcp(主機:端口)/gin?
    // charset=utf8mb4 設置字符集
    // parseTime=True 處理 time.Time 
    // loc=Local 時區設置,與本地時區保持一致
    dsn := "root:123456@tcp(127.0.0.1:3306)/gin?charset=utf8mb4&parseTime=True&loc=Local"
    // Open 第一個參數: 數據庫
    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
        SkipDefaultTransaction: true, // 事務配置
    })
    if err != nil {
        fmt.Println(err)
    }
}

orm 操作表

type User struct {
    ID int
    UserName string
    Age int
}

// 自定義表名
func (User) TableName() string{
    return "my_user"
}

// 創建表 表名為 結構體 小寫復數 users
DB.CreateTable(&User{})
// 創建自定義名字的 表
DB.Table("user1").CteateTable(&User{})

// 刪除表
DB.DropTable(&User{})
DB.DropTable("user1")

// 判斷表是否存在
DB.HasTable(&USer{})

// 給表重命名
DB.RenameTable(oldName, newName)

對表里面的數據 CRUD

// 創建 users 表
DB.CreateTable(&User{})

// 新增 data
DB.Create(&User{UserName: "yym", Age: 18})

// 查詢 data
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.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);
DB.First(&user, "id=?", "2121_aaa") // select * from users where id = "2121_aaa";
var user = User{ID: 10}
db.First(&user) // select * from users where id = 10
var result User
db.Model(User{ID: 10}).First(&result)  // SELECT * FROM users WHERE id = 10;

DB.Find(&users) // select * from users;

DB.Where("name=?", "yym").First(&user) // select * from users where name = 'yym' order by id limit 1 
DB.Where("name like ?", "%jin%").Find(&users) // select * from users where name like '%jin';
DB.Where("name=? and age>?", "yym", "10").Find(&user) // select * from users where name = 'yym' and age > 10;

// 更新 data, 先查詢, 再更新
DB.First(&user)
user.Name = "yym2"
user.Age = 100
DB.Save(&user) // update users set name = 'yym2', age=100 where id=1;

// 更新單個字段
DB.Model(&user).Updata("age", 20) // update users set age=20 where id=1;
DB.Model(&user).Where("active=?", true).Update("name", "hello") // update users set name='hello' where id=1 and active=true;
// 更新多個字段, struct map[string]interface{}
DB.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false}) // update users set name='hello', age=18 where id =1;

// 刪除 data
DB.Delete(&user) // delete from users where id = 1
DB.Where("name=?", "yym").Delete(&user)// delete from users where id =1 and name = 'yym';
DB.Delete(&Users{}, []int{1,2,3}) // delete from users where id in (1,2,3)

結構體和表名的映射

  1. 結構體沒有駝峰命名, 表名就是: 結構體名小寫+復數 => User 對應表 users
  2. 有駝峰命名, 表名: 大寫變小寫前面加下劃線,最后復數 => UserInfo 對應表 user_infos
  3. 有連續大寫字母, 表名: 連續大寫字母變小寫, 駝峰前加下劃線小寫復數 => DBUserInfo 對應 表 db_user_infos
// 對應表名 users
type User struct {}

// 對應表名 user_infos
type UserInfo struct {}

// 對應表名 db_user_infos
type DBUserInfo struct {}

gorm.Model

GORM 定義一個 gorm.Model 結構體,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt

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

type User struct {
    gorm.Model
    Name string
}
// 等效于
type User struct {
    ID        uint           `gorm:"primaryKey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
    Name string
}

字段標簽

聲明 model 時,tag 是可選的,GORM 支持以下 tag: tag 名大小寫不敏感,但建議使用 camelCase 風格

  • "primaryKey" 將列定義為主鍵
  • "autoIncrement" 自增
  • "-" 忽略該字段
  • "not null" 指定列為 NOT NULL
  • "index" 根據參數創建索引
  • "unique" 唯一索引, 不能重復
  • "column" 指定列名
  • "size" 設置默認長度
type Student struct {
    ID int `gorm:"primaryKey;autoIncrement"` // 主鍵 自動增長
    Name string `gorm:"index:idx_name;column:name1"` // 自定義索引名稱
    Desc string `gorm:"-;size:10"` // 忽略不映射這個字段
}

表關系

一對一

// 一對一: 一個用戶有一個擴展信息, 一個擴展信息對應一個用戶, 外鍵可以加在任意表中
// belongs_to : 關系和外鍵的指定在同一方

// 用戶表
type User struct {
    UserId string `gorm:"primaryKey;AutoIncrement"`
    Name string // 姓名
    Age int // 年齡
}

// 用戶信息表
// `gorm:"foreignKey:MyUserId;AssociationForeignKey:UserId"` 指定外鍵
type UserInfo struct {
    InfoId int `gorm: primaryKey;AutoIncrement"`
    Pic string // 圖片
    Address string
    User User `gorm:"foreignKey:MyUserId;AssociationForeignKey:UserId"` // 關聯關系
    MyUserId int  // 指定外鍵
}


// has_one: 關系和外鍵的指不在同一方
type User struct {
    UserId string `gorm:"primaryKey;AutoIncrement"`
    Name string // 姓名 
    Age int // 年齡
    UserInfo UserInfo `gorm:"foreignKey:MyUserId"`
}

// 
type UserInfo struct {
    InfoId int `gorm: primaryKey;AutoIncrement"`
    Pic string // 圖片 
    Address string 
    MyUserId int  // 指定外鍵
}

一對多

// 作者
type Author struct {
    AID int `gorm:"primaryKey"`
    Name string
    Age int
    Article []Article // 關聯關系
}
// 文章
type Article struct {
    ArID int `gorm:"primaryKey"`
    Title string
    Content string
    AID uint // 外鍵
    
}

// 一對多, 一個作者可以有多個文章, 一片文章屬于一個作者
// 在文章表中加一個 所屬作者 的 外鍵

多對多

// 學生
type Student struct {
    SId int `gorm:"primaryKey"`
    Name string
    // 關聯表
    Course []Course `gorm:"many2many:student_courses;"`
}

// 課程
type Course struct {
    CId int `gorm:"primaryKey"`
    teacher string
}

// 多對多, 一個學生有有個課程, 每個課程有很多學生上
// 新建一張關聯表 student_schedules: id student_id schedule_id

表關聯操作

一對一關聯操作

// add 關聯添加操作
info1 := UserInfo{
    Pic: "/xxx",
    Address: "上海",
    User: User{
        Name: "yym",
        Age: 18
    }
}

DB.create(&info1)

// find 關聯查詢操作 關聯關系在 UserInfo 中, 所以從 info 入手
var userinfo UserInfo // userinfo 是源模型, 主鍵不能為空
// 1. Association
DB.First(&userinfo, "info_id=?", 1) // 查不到關聯關系
// Model參數: 要查詢的表數據
// Association參數, 關聯到具體的模型名 User
// Find參數: 查詢的數據要放在什么字段中
DB.Model(&userinfo).Association("User").Find(&userinfo.User)
// 帶的條件的查詢
codes := []string{"zh-CN", "en-US", "ja-JP"}
DB.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages)

// 2. Preload 方式 預加載
DB.Preload("User").Find(&userinfo, "info_id=?", 1)

// 3. Related 方式
DB.First(&userinfo, "info_id=?", 1)
var user User
// 通過 userinfo 查出來的 User 字段信息放入新的容器 User 中
DB.Model(&userinfo).Related(&user, "User")

// update 關聯更新 通過 info 對 user 表數據更新
DB.Preload("User").Find(&userinfo "info_id", 1) // 查詢
DB.Model(&userinfo.user).Update("age", 20) // 更新

// delete 關聯刪除
DB.Preload("User").Find(&userinfo "info_id", 1) // 查詢
DB.Delete(&userinfo.User) // 刪除userinfo 的 User 表中的記錄

一對多關聯操作

// author article 兩個表, 一個 author -> 多個 article, 操作 author 表

// add 關聯添加
author := Author{
    Name: "yym",
    Age: 18,
    Article: []Atricle{
        {
            Title: "HTML",
            Content: "122"
        },
        {
            Content: "CSS"
            Content: "122"
        }
    },
}

DB.Create(&author)

// find 查詢
var author Author
// 條件是 文章的條件
DB.Model(&author).Where("ar_id=?", 1).Association("Article").Find(&author.Article)

// update 更新
// 先查詢, 再更新
DB.Preload("Article").Find(&author, "a_id=?", 1)
DB.Model(&author.Article).Where("ar_id=?", 1).Update("title", "JS入門")

// 刪除
DB.Where("ar_id=?", 2).Delete(&author.Article)

多對多關聯操作

// student course兩個表 關聯關系在 student 中, 操作 student

// add
stu := Student{
    Name: "yym",
    Course: []Course{
        {
            teacher: "張三",
        },{
            teacher: "李四"
                }
    }
}

DB.Create(&stu)

常用方法

DB.First() // 按條件查詢, 升序排列 查詢出一條記錄

// 有對應數據, 就查出來, 沒有, 就創建
DB.Where(User{Name: "non_existing"}).Attrs(User{Email: "fake@fake.org"}).FirstOrCreate(&user)

DB.Last() // 按條件查詢, 降序排列 查詢出一條記錄

DB.Take() // 按條件查詢, 和 First 類似, 未排序

DB.Find(&user, 1) // select * from where id = 1

DB.Where() // 查詢 加入指定條件 

DB.Select("name, age") // 對字段過濾

DB.Create() // 添加單條數據

DB.Save() // 保存更新數據

DB.Update() // 更新一列
DB.Updates() // 更新多列

DB.Delete() // 刪除數據

DB.Not("user_id = ?", 1).Find(&users) // 排除 id = 1 的值
DB.Or("user_id=?", 2) // 多個條件的查詢
DB.Order("age desc") // 升序或降序排列

DB.Limit(10) // 獲取記錄的最大數量
DB.Offset(1).Limit(3) // 跳過幾條,偏移, 和 Limit 結合使用
// 結果掃描到另一個結構體, 可以對 JSON 進行處理
DB.Scan(&user2) // 把獲取的數據掃描到 &user2 中, 結構體字段一致

DB.Count(&count) // 計數
// select age count(*) from users group by age
DB.Group("age") // 根據年齡進行分組
// select age, count(*) from users group by age having (age > 18)
DB.Having("age >18") // 分組以后進行過濾


// 左連接 右連接
// 左連接 users 表是全的, user_infos 表數據不全
//select * from users left join user_infos on users.id = user_infos.info_id

// 右連接 users 表不全, user_infos 表是全的
//select * from users right join user_infos on users.id = user_infos.info_id
type NewUserInfo struct {} // 新的結構體, 掃描數據進來
DB.Joins("left join user_infos on users.id = user_infos.info_id").Find(&users).Scan(&NewUserInfo)

DB.DeBug() // 打印當前行的 sql 語句

// 日志級別
logger.Default.LogMode(logger.Silent)

// 操作原生 sql 查詢 Raw
DB.Raw("select * from users").Find(&users)
DB.Raw("select * from users where age = ?", 14).Find(&users)
// 操作原生sql Exec 增加 刪除 修改
DB.Exec("insert into users (age, name) values (?, ?)", 33, "張五")
DB.Exec("delete from users where user_id = ?", 1)
DB.Exec("update users set name = ? where user_id = ?", "張三", 3)

日志

Golang標準庫的日志框架比較簡單, 使用第三方日志 logrus

go get -u github.com/sirupsen/logrus
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容