这是我参与「第五届青训营」伴学笔记创作活动的第6天
1 理解database/sql
1、基本用法
import (
"database/sql"
)
func main() {
// 使用driver+DSN初始化DB链接
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
// 执行SQL语句,通过rows取返回的数据
rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
}
// 处理完毕,释放连接
defer rows.Close()
// 数据和错误处理
var users []User
for rows.Next() {
var user User
err := rows.Scan(&user.ID, &user.Name)
if err != nil {
}
users = append(users, user)
}
// 错误处理
if rows.Err() != nil {
}
}
2、设计原理
在中间的database/sql层中,会对连接池进行操作,为了建立与数据库新的连接,可以直接从连接池中复用已存在的连接,或者直接新建新的连接,当当前连接使用完后,还会放回到连接池中defer dc.db.putConn(dc,err,true)
2 GORM
go get时出现问题:
go get -u github.com/jinzhu/gorm
go: module github.com/jinzhu/gorm: Get "https://proxy.golang.org/github.com/jinzhu/gorm/@v/list": dial tcp 142.251.43.17:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
解决办法:配置代理 go env -w GOPROXY=https://goproxy.cn
1、基本用法
连接数据库
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
db, err := gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/3306"))
var users []User
err := db.Select("id", "name").Find(&users, 1).Error
}
CRUD
// 操作数据库
db.AutoMigrate(&Product{})
db.Migrator().CreateTable(&Product{})
// 创建
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user)
// 返回主键、error、影响的行数
user.ID
result.Error
result.RowsAffected
// 批量创建
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}}
db.Create(&user)
db.CreateInBatches(users, 100)
for _, user := range users {
user.ID
}
// 读取
var product Product
db.First(&product, 1)
db.First(&product, "code = ?", "L1212")
result := db.Find(&users, []int{1, 2, 3})
result.RowsAffected // 返回找到的记录数
errors.Is(result.Error, gorm.ErrRecordNotFound) // First Last Take查不到数据
// 更新某个字段
db.Model(&product).Update("price", 2000)
db.Model(&product).UpdateColumn("Price", 2000)
// 更新多个字段
db.Model(&product).Updates(Product{Price: 2000, Code: "L1212"})
db.Model(&product).Updates(map[string]interface{}{"Price": 2000, "Code": "L1212"})
// 批量更新
db.Model(&Product{}).Where("price < ?", 2000).Updates(map[string]interface{}{"price": 2000})
// 删除
db.Delete(&product)
关联
1、关联操作CRUD
// 保存用户及关联
db.Save(&User {
Name: "jinzhu",
Languages: []Language{{Name: "zh-CN"}, {Name: "en-US"}},
})
// 关联模式
langAssociation := db.Model(&user).Association("Languages")
// 查询关联
langAssociation.Find(&langAssociation)
langAssociation.Append([]Language{languageZH, languageEN})
langAssociation.Replace([]language{languageZH, languageDE})
langAssociation.Delete(languageZh, langugeEN)
langAssociation.Clear()
langAssociation.Count()
// 批量模式
var users []User{user1, user2, user3}
langAssociation := db.Model(&users).Association("Languages")
db.Model(&users).Association("Team").Append(&userA, userB, &[]User{UserA, UserB})
2、Preload、Joins预加载
// 查询用户时找出其他订单,个人信息
db.Preload("Orders").Preload("Profile").Find(&users)
// 使用Join SQL加载
db.Joins("Company").Joins("Manager").First(&user, 1)
db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
// 预加载全部关联
db.Preload(clause.Associations).Find(&users)
// 多级预加载
db.Preload("Orders.OrderItems.Product").Find(&users)
db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)
// 查询用户时找出未取消的订单
db.Preload("Orsers", "state NOT IN(?)", "cancelled").Find(&users)
db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC")
}).Find(&users)
3、级联删除
// 方法1.使用数据库约束自动删除
// 需要使用GORM Migrate数据库迁移数据库外键
db.AutoMigrate(&User{})
// 如果未启用软删除,再删除User时会自动删除其依赖
db.Delete(&User{})
// 方法2:使用Select实现级联删除,不依赖数据库约束及软删除
db.Select("Account").Delete(&user)
db.Select("Orders", "CreditCards").Delete(&user)
db.Select(clause.Associations).Delete(&user)
2、设计原理
3、最佳实践
- 数据序列化与SQL表达式
- 批量数据操作
- 代码复用、分库分表、Sharding
- 混沌工程
- Logger/Trace
- Migrator
- Gen代码生成/Raw SQL
- 安全