ORM 提供了 DB
方法,可用于从当前 *gorm.DB
返回一个通用的数据库接口 *sql.DB
// 获取通用数据库对象 sql.DB,然后使用其提供的功能
sqlDB, err := db.DB()
// Ping
sqlDB.Ping()
// Close
sqlDB.Close()
// 返回数据库统计信息
sqlDB.Stats()
注意 :如果底层连接的数据库不是 *sql.DB
,它会返回错误
// 获取通用数据库对象 sql.DB ,然后使用其提供的功能
sqlDB, err := db.DB()
// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
2.处理错误
GORM 的错误处理与常见的 Go 代码不同,因为 GORM 提供的是链式 API。
如果遇到任何错误,GORM 会设置 *gorm.DB
的 Error
字段,您需要像这样检查它:
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// 处理错误...
}
或者
if result := db.Where("name = ?", "jinzhu").First(&user); result.Error != nil {
// 处理错误...
}
ErrRecordNotFound
当 First
、Last
、Take
方法找不到记录时,GORM 会返回 ErrRecordNotFound
错误。如果发生了多个错误,你可以通过 errors.Is
判断错误是否为 ErrRecordNotFound
,例如:
// 检查错误是否为 RecordNotFound
err := db.First(&user, 100).Error
errors.Is(err, gorm.ErrRecordNotFound)
3.自定义数据类型
GORM 提供了少量接口,使用户能够为 GORM 定义支持的数据类型,这里以 json 为例
实现自定义数据类型 Scanner / Valuer自定义的数据类型必须实现 Scanner 和 Valuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库
例如:
type JSON json.RawMessage
// 实现 sql.Scanner 接口,Scan 将 value 扫描至 Jsonb
func (j *JSON) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
}
result := json.RawMessage{}
err := json.Unmarshal(bytes, &result)
*j = JSON(result)
return err
}
// 实现 driver.Valuer 接口,Value 返回 json value
func (j JSON) Value() (driver.Value, error) {
if len(j) == 0 {
return nil, nil
}
return json.RawMessage(j).MarshalJSON()
有许多第三方包实现了 Scanner
/Valuer
接口,可与 GORM 一起使用,例如:
import (
"github.com/google/uuid"
"github.com/lib/pq"
)
type Post struct {
ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
Title string
Tags pq.StringArray `gorm:"type:text[]"`
}
GormDataTypeInterface
GORM 会从 type标签 中读取字段的数据库类型,如果找不到,则会检查该结构体是否实现了 GormDBDataTypeInterface
或 GormDataTypeInterface
接口,然后使用接口返回值作为数据类型
type GormDataTypeInterface interface {
GormDataType() string
}
type GormDBDataTypeInterface interface {
GormDBDataType(*gorm.DB, *schema.Field) string
}
func (JSON) GormDataType() string {
return "json"
}
type User struct {
Attrs JSON
}
func (user User) BeforeCreate(tx *gorm.DB) {
field := tx.Statement.Schema.LookUpField("Attrs")
if field.DataType == "json" {
// 做点什么...
}
}
在迁移时,GormDBDataType
通常会为当前驱动返回恰当的数据类型,例如:
func (JSON) GormDBDataType(db *gorm.DB, field *schema.Field) string {
// 使用 field.Tag、field.TagSettings 获取字段的 tag
// 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 获取全部的选项
// 根据不同的数据库驱动返回不同的数据类型
switch db.Dialector.Name() {
case "mysql", "sqlite":
return "JSON"
case "postgres":
return "JSONB"
}
return ""
}
如果 struct 没有实现 GormDBDataTypeInterface
或 GormDataTypeInterface
接口,GORM 会根据 struct 第一个字段推测其数据类型,例如:会为 NullString
使用 string
type NullString struct {
String string // 使用第一个字段的数据类型
Valid bool
}
type User struct {
Name NullString // 数据类型会是 string
}
GormValuerInterface
GORM 提供了 GormValuerInterface
接口,支持使用 SQL 表达式或基于 context 的值进行 create/update,例如:
// GORM Valuer interface
type GormValuerInterface interface {
GormValue(ctx context.Context, db *gorm.DB) clause.Expr
}
使用 SQL 表达式进行 Create/Update
type Location struct {
X, Y int
}
func (loc Location) GormDataType() string {
return "geometry"
}
func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
return clause.Expr{
SQL: "ST_PointFromText(?)",
Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
}
}
// Scan 方法实现了 sql.Scanner 接口
func (loc *Location) Scan(v interface{}) error {
// Scan a value into struct from database driver
}
type User struct {
ID int
Name string
Location Location
}
db.Create(&User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`point`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))
db.Model(&User{ID: 1}).Updates(User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// UPDATE `user_with_points` SET `name`="jinzhu",`location`=ST_PointFromText("POINT(100 100)") WHERE `id` = 1
你也可以根据 SQL 表达式进行 create/update,查看 Create From SQL Expr 和 Update with SQL Expression 获取详情
基于 Context 的值
如果你想创建或更新一个依赖于当前 context 的值,你也可以实现 GormValuerInterface 接口,例如:
type EncryptedString struct {
Value string
}
func (es EncryptedString) GormValue(ctx context.Context, db *gorm.DB) (expr clause.Expr) {
if encryptionKey, ok := ctx.Value("TenantEncryptionKey").(string); ok {
return clause.Expr{SQL: "?", Vars: []interface{}{Encrypt(es.Value, encryptionKey)}}
} else {
db.AddError(errors.New("invalid encryption key"))
}
return
}
条件表达式
如果你想构建一些查询 helper,你可以让 struct 实现 clause.Expression
接口:
type Expression interface {
Build(builder Builder)
}
4.查询
Scope 查询示例:
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
return db.Where("amount > ?", 1000)
}
func PaidWithCreditCard(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
}
func PaidWithCod(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
}
func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
return db.Where("status IN (?)", status)
}
}
db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// 查找所有金额大于 1000 的xyk订单
db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// 查找所有金额大于 1000 的 COD 订单
db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// 查找所有金额大于1000 的已付款或已发货订单
分页
func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
page, _ := strconv.Atoi(r.Query("page"))
if page == 0 {
page = 1
}
pageSize, _ := strconv.Atoi(r.Query("page_size"))
switch {
case pageSize > 100:
pageSize = 100
case pageSize <= 0:
pageSize = 10
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
db.Scopes(Paginate(r)).Find(&users)
db.Scopes(Paginate(r)).Find(&articles)
更新
Scope 更新、删除示例:
func CurOrganization(r *http.Request) func(db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
org := r.Query("org")
if org != "" {
var organization Organization
if db.Session(&Session{}).First(&organization, "name = ?", org).Error == nil {
return db.Where("org_id = ?", organization.ID)
}
}
db.AddError("invalid organization")
return db
}
}
db.Model(&article).Scopes(CurOrganization(r)).Update("Name", "name 1")
// UPDATE articles SET name = "name 1" WHERE org_id = 111
db.Scopes(CurOrganization(r)).Delete(&Article{})
// DELETE FROM articles WHERE org_id = 111
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)