Gorm

GORM #

特性 #

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

安装 #

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

快速入门 #

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // 迁移 schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // 根据整形主键查找
  db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

  // Update - 将 product 的 price 更新为 200
  db.Model(&product).Update("Price", 200)
  // Update - 更新多个字段
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - 删除 product
  db.Delete(&product, 1)
}

字段标签 #

标签是声明模型时可选的标记,标记不区分大小写,GORM 支持以下标记:

声明model时,tag是可选的,GORM支持以下tag:tag名大小写不敏感,但建议使用camelcase风格

标签名 说明
column 指定列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持bool、int、uint、float、string、time、bytes并且可以和其他标签一起使用,例如:not null、size、autoIncrement…像varbinary(8)这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT、UNSIGNED、not、NULL、AUTO、INSTREMENT
size 指定列大小,例如:size: 256
primaryKey 指定列为主键
unique 指定列为唯一
default 指定列的默认值
precision 指定列的精度
scale 指定列大小
not null 不能为空
autolncrement 指定列为自动增长
embedded 嵌套字段
embeddedPrefix 嵌入字段的列名前缀
autoCreateTime 创建时追踪当前时间,对于int字段,它会追踪时间戳秒数,您可以使用nano/milli来追踪纳秒、毫秒时间戳,例如autoCreateTime:nano
autoUpdateTime 创建/更新时追踪当前时间,对于int字段,它会追踪时间戳秒数,您可以使用nano/milli来追踪纳秒、毫秒时间戳,例如:autoupdateTime:milli
index 根据参数创建索引1,多个字段使用相同的名称则创建复合索引,查看索引获取详情
uniqueindex 与index相同,但创建的是唯一索引
check 创建检查约束,例如check:age>13查看约束获取详情
<- 设置字段写入的权限,<-:create只创建、<-:update只更新、<-:false无写入权限、「<-创建和更新权限
-> 设置字段读的权限,->:false无读权限
- 忽略该字段,-无读写权限

gorm使用中遇到的坑点 #

3、Count方法不适合放在raw方法后面,否则将会出错

count:=0
db.Raw(sql).Count(&count)

正确用法

db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
//正确用法
result := engine.Raw(querySQL, args...).Find(resultData)
	if result.Error != nil {
		err = fmt.Errorf("find data in table error:%s", err.Error())
		return
	} else {
		count = result.RowsAffected
	}

https://blog.csdn.net/weixin_44267448/article/details/104271850

limit默认值如果为0,则需要手动赋值为-1,否则查不出来

eng := engine.Table(tableName).Where("pid=?", pid)
err = eng.Limit(limit).Offset(skip).Count(&nCount).Find(&resultData).Error

输入框对”%“ ”_“特殊字符的处理 #

在 SQL 中,_% 是通配符,用于 LIKE 操作符中的字符串匹配。以下是它们的含义:

%:匹配任意多个字符

  • % 可以匹配零个或多个字符。

  • 示例:

    name LIKE 'user%'
    

    匹配:user, user1, user_abc, userXYZ, 等等。

    查询:

    name LIKE '%test%'
    

    匹配:test, mytest, unittesting, 等等。

_:匹配单个字符

  • _ 可以匹配任意单个字符。

  • 示例:

    查询:

    name LIKE 'user_'
    

    匹配:user1, userA, user_,但不匹配 user12user.

    查询:

    name LIKE '_test'
    

    匹配:1test, A_test, Ztest,但不匹配 ABtest.

if strings.Contains(name, "_") {
		name = strings.ReplaceAll(name, "_", "\\_")
	}
	if strings.Contains(name, "%") {
		name = strings.ReplaceAll(name, "%", "\\%")
	}    
func (es *RecordService) GetRecords(limit, offset int, name, evidenceType string) (count int64, evidences []vmodel.Evidence, err error) {
	var items []string
	var args []interface{}
	if name != "" {
		items = append(items, "(`name` like ? ESCAPE '\\')")
		args = append(args, "%"+name+"%")
	}
	if evidenceType != "" {
		items = append(items, "(`type` like ?)")
		args = append(args, "%"+evidenceType+"%")
	}
	keywordSQL := strings.Join(items, " and ")

	count, evidences, err = proxy.Record.GetRecords(limit, offset, keywordSQL, args, false)
	if err != nil {
		common.Log.Error(err)
		return
	}
	return
}
func (ep *RecordProxy) GetRecords(limit, offset int, keywordSQL string, args []interface{}, isAll bool) (count int64, evidences []vmodel.Evidence, err error) {

	masterDBProxy, err := db.GetDBInstance().GetMasterDB()
	if err != nil {
		return
	}

	if isAll {
		err = masterDBProxy.Model(&vmodel.Evidence{}).Unscoped().Where(keywordSQL, args...).Count(&count).Limit(limit).Offset(offset).Find(&evidences).Error
	} else {
		//为了防止更改磁盘信息导致查不出记录列表 只查询用到的列
		err = masterDBProxy.Model(&vmodel.Evidence{}).Where(keywordSQL, args...).Select("id,case_id,name,brand_name,type,evidence_infos,partitions,brand_type,location,create_at,update_at,disk_info").Count(&count).Limit(limit).Offset(offset).Order("update_at desc").Find(&evidences).Error
	}

	return
}