什么是 ORM
ORM(Object-Relational-Mapping)
是在關(guān)系型數(shù)據(jù)庫和對(duì)象之間做一個(gè)映射, 在操作數(shù)據(jù)庫時(shí)
就不需要和復(fù)雜的 sql
語句打交道, 而是像操作對(duì)象一樣操作sql就可以
GORM
是 go
語言的一個(gè) orm
框架
# 使用 mysql 創(chuàng)建一個(gè) users 表
create table users
(
id int(11),
username varchar(255),
age int(3),
sex int
);
// 使用 gorm 對(duì)象創(chuàng)建一個(gè) users 表
type User struct {
ID int
UserName string
Age int
Sex string
}
DB.CreateTable(&User{}) // 創(chuàng)建表
安裝
# 安裝 gorm
go get -u gorm.io/gorm
# 安裝 mysql 驅(qū)動(dòng)
go get gorm.io/driver/mysql
連接數(shù)據(jù)庫 mysql
var (
DB *gorm.DB
err error
)
func init() {
// 參考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 獲取詳情
// 數(shù)據(jù)庫用戶名:密碼@tcp(主機(jī):端口)/gin?
// charset=utf8mb4 設(shè)置字符集
// parseTime=True 處理 time.Time
// loc=Local 時(shí)區(qū)設(shè)置,與本地時(shí)區(qū)保持一致
dsn := "root:123456@tcp(127.0.0.1:3306)/gin?charset=utf8mb4&parseTime=True&loc=Local"
// Open 第一個(gè)參數(shù): 數(shù)據(jù)庫
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true, // 事務(wù)配置
})
if err != nil {
fmt.Println(err)
}
}
orm 操作表
type User struct {
ID int
UserName string
Age int
}
// 自定義表名
func (User) TableName() string{
return "my_user"
}
// 創(chuàng)建表 表名為 結(jié)構(gòu)體 小寫復(fù)數(shù) users
DB.CreateTable(&User{})
// 創(chuàng)建自定義名字的 表
DB.Table("user1").CteateTable(&User{})
// 刪除表
DB.DropTable(&User{})
DB.DropTable("user1")
// 判斷表是否存在
DB.HasTable(&USer{})
// 給表重命名
DB.RenameTable(oldName, newName)
對(duì)表里面的數(shù)據(jù) CRUD
// 創(chuàng)建 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;
// 更新單個(gè)字段
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;
// 更新多個(gè)字段, 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)
結(jié)構(gòu)體和表名的映射
- 結(jié)構(gòu)體沒有駝峰命名, 表名就是: 結(jié)構(gòu)體名小寫+復(fù)數(shù) => User 對(duì)應(yīng)表 users
- 有駝峰命名, 表名: 大寫變小寫前面加下劃線,最后復(fù)數(shù) => UserInfo 對(duì)應(yīng)表 user_infos
- 有連續(xù)大寫字母, 表名: 連續(xù)大寫字母變小寫, 駝峰前加下劃線小寫復(fù)數(shù) => DBUserInfo 對(duì)應(yīng) 表 db_user_infos
// 對(duì)應(yīng)表名 users
type User struct {}
// 對(duì)應(yīng)表名 user_infos
type UserInfo struct {}
// 對(duì)應(yīng)表名 db_user_infos
type DBUserInfo struct {}
gorm.Model
GORM 定義一個(gè) gorm.Model
結(jié)構(gòu)體刃麸,其包括字段 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
}
字段標(biāo)簽
聲明 model
時(shí)颊糜,tag
是可選的阿逃,GORM 支持以下 tag: tag 名大小寫不敏感先慷,但建議使用 camelCase 風(fēng)格
- "primaryKey" 將列定義為主鍵
- "autoIncrement" 自增
- "-" 忽略該字段
- "not null" 指定列為 NOT NULL
- "index" 根據(jù)參數(shù)創(chuàng)建索引
- "unique" 唯一索引, 不能重復(fù)
- "column" 指定列名
- "size" 設(shè)置默認(rèn)長(zhǎng)度
type Student struct {
ID int `gorm:"primaryKey;autoIncrement"` // 主鍵 自動(dòng)增長(zhǎng)
Name string `gorm:"index:idx_name;column:name1"` // 自定義索引名稱
Desc string `gorm:"-;size:10"` // 忽略不映射這個(gè)字段
}
表關(guān)系
一對(duì)一
// 一對(duì)一: 一個(gè)用戶有一個(gè)擴(kuò)展信息, 一個(gè)擴(kuò)展信息對(duì)應(yīng)一個(gè)用戶, 外鍵可以加在任意表中
// belongs_to : 關(guān)系和外鍵的指定在同一方
// 用戶表
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"` // 關(guān)聯(lián)關(guān)系
MyUserId int // 指定外鍵
}
// has_one: 關(guān)系和外鍵的指不在同一方
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 // 指定外鍵
}
一對(duì)多
// 作者
type Author struct {
AID int `gorm:"primaryKey"`
Name string
Age int
Article []Article // 關(guān)聯(lián)關(guān)系
}
// 文章
type Article struct {
ArID int `gorm:"primaryKey"`
Title string
Content string
AID uint // 外鍵
}
// 一對(duì)多, 一個(gè)作者可以有多個(gè)文章, 一片文章屬于一個(gè)作者
// 在文章表中加一個(gè) 所屬作者 的 外鍵
多對(duì)多
// 學(xué)生
type Student struct {
SId int `gorm:"primaryKey"`
Name string
// 關(guān)聯(lián)表
Course []Course `gorm:"many2many:student_courses;"`
}
// 課程
type Course struct {
CId int `gorm:"primaryKey"`
teacher string
}
// 多對(duì)多, 一個(gè)學(xué)生有有個(gè)課程, 每個(gè)課程有很多學(xué)生上
// 新建一張關(guān)聯(lián)表 student_schedules: id student_id schedule_id
表關(guān)聯(lián)操作
一對(duì)一關(guān)聯(lián)操作
// add 關(guān)聯(lián)添加操作
info1 := UserInfo{
Pic: "/xxx",
Address: "上海",
User: User{
Name: "yym",
Age: 18
}
}
DB.create(&info1)
// find 關(guān)聯(lián)查詢操作 關(guān)聯(lián)關(guān)系在 UserInfo 中, 所以從 info 入手
var userinfo UserInfo // userinfo 是源模型, 主鍵不能為空
// 1. Association
DB.First(&userinfo, "info_id=?", 1) // 查不到關(guān)聯(lián)關(guān)系
// Model參數(shù): 要查詢的表數(shù)據(jù)
// Association參數(shù), 關(guān)聯(lián)到具體的模型名 User
// Find參數(shù): 查詢的數(shù)據(jù)要放在什么字段中
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 方式 預(yù)加載
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 關(guān)聯(lián)更新 通過 info 對(duì) user 表數(shù)據(jù)更新
DB.Preload("User").Find(&userinfo "info_id", 1) // 查詢
DB.Model(&userinfo.user).Update("age", 20) // 更新
// delete 關(guān)聯(lián)刪除
DB.Preload("User").Find(&userinfo "info_id", 1) // 查詢
DB.Delete(&userinfo.User) // 刪除userinfo 的 User 表中的記錄
一對(duì)多關(guān)聯(lián)操作
// author article 兩個(gè)表, 一個(gè) author -> 多個(gè) article, 操作 author 表
// add 關(guān)聯(lián)添加
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)
多對(duì)多關(guān)聯(lián)操作
// student course兩個(gè)表 關(guān)聯(lián)關(guān)系在 student 中, 操作 student
// add
stu := Student{
Name: "yym",
Course: []Course{
{
teacher: "張三",
},{
teacher: "李四"
}
}
}
DB.Create(&stu)
常用方法
DB.First() // 按條件查詢, 升序排列 查詢出一條記錄
// 有對(duì)應(yīng)數(shù)據(jù), 就查出來, 沒有, 就創(chuàng)建
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") // 對(duì)字段過濾
DB.Create() // 添加單條數(shù)據(jù)
DB.Save() // 保存更新數(shù)據(jù)
DB.Update() // 更新一列
DB.Updates() // 更新多列
DB.Delete() // 刪除數(shù)據(jù)
DB.Not("user_id = ?", 1).Find(&users) // 排除 id = 1 的值
DB.Or("user_id=?", 2) // 多個(gè)條件的查詢
DB.Order("age desc") // 升序或降序排列
DB.Limit(10) // 獲取記錄的最大數(shù)量
DB.Offset(1).Limit(3) // 跳過幾條,偏移, 和 Limit 結(jié)合使用
// 結(jié)果掃描到另一個(gè)結(jié)構(gòu)體, 可以對(duì) JSON 進(jìn)行處理
DB.Scan(&user2) // 把獲取的數(shù)據(jù)掃描到 &user2 中, 結(jié)構(gòu)體字段一致
DB.Count(&count) // 計(jì)數(shù)
// select age count(*) from users group by age
DB.Group("age") // 根據(jù)年齡進(jìn)行分組
// select age, count(*) from users group by age having (age > 18)
DB.Having("age >18") // 分組以后進(jìn)行過濾
// 左連接 右連接
// 左連接 users 表是全的, user_infos 表數(shù)據(jù)不全
//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 {} // 新的結(jié)構(gòu)體, 掃描數(shù)據(jù)進(jìn)來
DB.Joins("left join user_infos on users.id = user_infos.info_id").Find(&users).Scan(&NewUserInfo)
DB.DeBug() // 打印當(dāng)前行的 sql 語句
// 日志級(jí)別
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標(biāo)準(zhǔn)庫的日志框架比較簡(jiǎn)單, 使用第三方日志 logrus
go get -u github.com/sirupsen/logrus