GO orm 框架GORM学习
1. 介绍
gorm
是一个使用Go
语言编写的ORM
框架。它文档齐全,对开发者友好,支持主流数据库。官方中文文档 https://gorm.io/zh_CN/docs/index.html
2. 安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
3. 连接MYSQL
GORM
官方支持的数据库类型有:MySQL, PostgreSQL, SQlite, SQL Server
,这里只讲常见的MYSQL
。其他类型数据库参见官方文档。
3.1 安装mysql
依赖
go get -u gorm.io/driver/mysql
3.2 建立连接
a. 默认连接
// 默认连接
func ConnectMysqlByDefault(host, port, user, pass, dbname string) (*gorm.DB, error) {
// user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
dns := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
user, pass, host, port, dbname)
return gorm.Open(mysql.Open(dns), &gorm.Config{})
}
b. 自定义配置连接
// 自定义配置连接
func ConnectMysqlByCustom(host, port, user, pass, dbname string) (*gorm.DB, error) {
// user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
dns := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
user, pass, host, port, dbname)
return gorm.Open(mysql.New(mysql.Config{
DSN: dns,
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, //禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, //重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, //用change重命名列,MySQL8之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}))
}
c. 基于database/sql
连接
// 基于database/sql连接,进行二次封装
func ConnectMysqlByDatabaseSql(host, port, user, pass, dbname string) (*gorm.DB, error) {
dns := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
user, pass, host, port, dbname)
db, err := sql.Open("mysql", dns)
if err != nil {
return nil, err
}
return gorm.Open(mysql.New(mysql.Config{Conn: db}))
}
gorm.Config
配置使用,参见官方文档: https://gorm.io/zh_CN/docs/gorm_config.html
4. 声明模型
4.1 什么是模型
模型其实就是标准的结构体(struct
),其属性和表结构的字段按照 gorm约定 一一对应。约定如下:
gorm
使用ID
作为主键,对应表中的id
。- 使用结构体名的
蛇形复数
作为表名。对于结构体User
,根据约定,其表名为users
- 使用
CreatedAt
、UpdatedAt
字段追踪创建、更新时间。
gorm
定义一个 gorm.Model
结构体,其包括字段 ID
、CreatedAt
、UpdatedAt
、DeletedAt
,其他结构体直接匿名嵌套即可。
4.2 字段标签
标签名 | 说明 |
---|---|
column |
指定 db 列名 |
type |
列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null 、size , autoIncrement … 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT |
size |
指定列大小,例如:size:256 |
primaryKey |
指定列为主键 |
unique |
指定列为唯一 |
default |
指定列的默认值 |
not null |
指定列为 NOT NULL |
autoIncrement |
指定列为自动增长 |
comment |
迁移时为字段添加注释 |
index |
根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 |
更多约定,请查看官方文档
4.3 创建用户模型
按照gorm
约定创建用户模型,便于后面使用。用户模型如下
// 用户模型
type User struct {
gorm.Model
NickName string `gorm:"type:varchar(20);not null;default:'';comment:昵称"`
Age uint8 `gorm:"size:4;comment:年龄"`
Phone string `gorm:"type:char(11);index:un_phone;comment:手机号"`
MemberNumber string `gorm:"type:varchar(20);index:un_phone;comment:会员编号"`
Birthday sql.NullString `gorm:"type:varchar(10);comment:生日"`
ActivatedAt sql.NullTime `gorm:"comment:激活时间"`
}
5. 迁移
5.1 根据模型生成表结构
根据上面创建的用户模型User
,生成对应的表结构
a. 代码
// 自动迁移schema,(根据结构体创建或者更新schema)
func GormAutoMigrate(host, port, use, pass, database string) error {
// ConnectMysqlByDefault 代码参见上面的: 默认连接
mysqlByDefault, err := ConnectMysqlByDefault(host, port, use, pass, database)
if err != nil {
return err
}
// 指定引擎和表备注
err = mysqlByDefault.Set("gorm:table_options", "ENGINE=InnoDB COMMENT='用户表'").AutoMigrate(&User{})
if err != nil {
return err
}
return nil
}
// 测试迁移
func TestAutoMigrate(t *testing.T) {
host := "127.0.0.1"
use,pass,port,database := "root","root","3306","test"
err := gorme.GormAutoMigrate(host, port, use, pass, database)
if err != nil {
t.Error(err)
}
fmt.Println("创建表结构完成!")
}
b. 生成的表结构如下:
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
`nick_name` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '昵称',
`age` tinyint unsigned DEFAULT NULL COMMENT '年龄',
`phone` char(11) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '手机号',
`member_number` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '会员编号',
`birthday` varchar(10) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '生日',
`activated_at` datetime(3) DEFAULT NULL COMMENT '激活时间',
PRIMARY KEY (`id`),
KEY `idx_users_deleted_at` (`deleted_at`),
KEY `un_phone` (`phone`,`member_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户表';
6.CRUD
6.1 初始化客户端
// 定义包内全局变量
var mysqlClient *gorm.DB
// 初始化mysql客户端
func init() {
// 连接客户端
host := "127.0.0.1"
user, pass, port, database := "root", "root", "3306", "test"
databaseSql, _ := gorme.ConnectMysqlByDatabaseSql(host, port, user, pass, database)
mysqlClient = databaseSql
}
6.2 单条插入
// 保存一条记录
func TestAddOne(t *testing.T) {
// 初始化结构体
userRow := gorme.User{
NickName: "李四",
Age: 18,
Phone: "12340000",
MemberNumber: "A0001",
Birthday: sql.NullString{String: "1991-03-04",Valid: true},
ActivatedAt: sql.NullTime{Time: time.Now(),Valid: true},
}
// 传入指针
result := mysqlClient.Create(&userRow)
fmt.Println(result)
fmt.Println("id: ",userRow.ID)
fmt.Println("插入记录错误: ",result.Error)
fmt.Println("插入记录的条数: ",result.RowsAffected)
}
/*** 输出
=== RUN TestAddOne
&{0xc000184240 <nil> 1 0xc0001a21c0 0}
id: 1
插入记录错误: <nil>
插入记录的条数: 1
--- PASS: TestAddOne (0.04s)
PASS
*/
6.3 批量插入
// 批量插入
func TestBatchInsert(t *testing.T) {
// 定义user 切片
userRows := []gorme.User{
{NickName: "路人甲", Age: 20, Phone: "20000000", MemberNumber: "A0002"},
{NickName: "路人乙", Age: 22, Phone: "30000000", MemberNumber: "A0003"},
{NickName: "路人丙", Age: 24, Phone: "40000000", MemberNumber: "A0004"},
}
// 保存
result := mysqlClient.Create(&userRows)
fmt.Println("插入记录错误: ", result.Error)
fmt.Println("插入记录的条数: ", result.RowsAffected)
// 打印ID
for _, row := range userRows {
fmt.Println("插入ID: ", row.ID)
}
}
/** 输出
=== RUN TestBatchInsert
插入记录错误: <nil>
插入记录的条数: 3
插入ID: 4
插入ID: 5
插入ID: 6
--- PASS: TestBatchInsert (0.02s)
PASS
*/
更新插入方式,参见文档: https://gorm.io/zh_CN/docs/create.html
6.4 查询
a. 单条记录: First
、Take
、Last
First
: 获取第一条记录(主键升序)Take
: 获取一条记录,没有指定排序字段Last
: 获取最后一条记录(主键降序)
// 使用提供的First、Take、Last,查询单条记录
func TestGetOne(t *testing.T) {
// 定义对应的结构体变量存储结果
var firstUser gorme.User
var taskUser gorme.User
var lastUser gorme.User
var result *gorm.DB
// 获取第一条记录(主键升序) SELECT * FROM users ORDER BY id LIMIT 1;
result = mysqlClient.First(&firstUser)
fmt.Printf("First Result: %+v\n", result.RowsAffected)
// 获取一条记录,没有指定排序字段 SELECT * FROM users LIMIT 1;
result = mysqlClient.Take(&taskUser)
fmt.Printf("Take Result: %+v\n", result.RowsAffected)
// 获取最后一条记录(主键降序)SELECT * FROM users ORDER BY id DESC LIMIT 1;
result = mysqlClient.Last(&lastUser)
fmt.Printf("lastUser Result: %+v\n", result.RowsAffected)
}
@注意: 如果表里没有数据,会报错result.Error返回:ErrRecordNotFound
,可以使用下面语句检查错误。如果想避免ErrRecordNotFound
错误,你可以使用Find
,如:db.Limit(1).Find(&user)
// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)
b. 多条记录: Find
// 使用Find(默认查询的是检索全部)
func TestGetByFind(t *testing.T) {
var userList []gorme.User
// 指针查询字段
result := mysqlClient.Select("id", "nick_name").Find(&userList)
for _, user := range userList {
fmt.Printf("id: %d nick_name: %s \n", user.ID,user.NickName)
}
fmt.Println("查询记录数: ", result.RowsAffected)
fmt.Println("查询错误: ", result.Error)
}
/** 输出
=== RUN TestGetByFind
id: 2 nick_name: 李四
id: 3 nick_name: 张三
id: 4 nick_name: 路人甲
id: 5 nick_name: 路人乙
id: 6 nick_name: 路人丙
查询记录数: 5
查询错误: <nil>
--- PASS: TestGetByFind (0.00s)
PASS
*/
c. 条件查询
1. String条件
// 根据String条件查询
func TestGetByStringWhere(t *testing.T) {
// 定义对应的结构体变量
var user gorme.User
var userList []gorme.User
var result *gorm.DB
// 字符串条件查询一条
result = mysqlClient.Where("nick_name = ?", "张三").First(&user)
fmt.Printf("Res1: %v err:%v \n", result.RowsAffected,result.Error)
// 字符串条件查询多条
result = mysqlClient.Where("nick_name <> ?", "张三").Find(&userList)
fmt.Printf("Res2: %v err:%v \n", result.RowsAffected,result.Error)
// 多个条件
result = mysqlClient.Where("nick_name = ? and age >= ?", "张三", 18).First(&user)
fmt.Printf("Res3: %v err:%v \n", result.RowsAffected,result.Error)
}
/** 输出
=== RUN TestGetByStringWhere
Res1: 1 err:<nil>
Res2: 4 err:<nil>
Res3: 1 err:<nil>
--- PASS: TestGetByStringWhere (0.02s)
PASS
*/
2. Struct和Map条件
// 根据struct和map 条件查询结果
func TestGetByStructAndMapWhere(t *testing.T) {
// 定义对应的结构体变量
var user gorme.User
var userList []gorme.User
var result *gorm.DB
// 结构体条件
result = mysqlClient.Where(&gorme.User{NickName: "张三",Age: 18}).First(&user)
fmt.Printf("结构体条件: %+v err:%v \n", result.RowsAffected,result.Error)
// map条件
result = mysqlClient.Where(map[string]interface{}{"age":18}).Find(&userList)
fmt.Printf("map条件: %+v err:%v \n", result.RowsAffected,result.Error)
// 主键切片
result = mysqlClient.Where([]int64{2,3,4,5}).Find(&userList)
fmt.Printf("主键切片: %+v err:%v \n", result.RowsAffected,result.Error)
}
/** 输出
=== RUN TestGetByStructAndMapWhere
结构体条件: 1 err:<nil>
map条件: 3 err:<nil>
主键切片: 4 err:<nil>
--- PASS: TestGetByStructAndMapWhere (0.01s)
PASS
*/
@注意 当使用结构作为条件查询时,
GORM
只会查询非零值字段。这意味着如果您的字段值为0
、''
、false
或其他 零值,该字段不会被用于构建查询条件,例如:db.Where(&User{NickName: "张三", Age: 0}).Find(&users) // SELECT * FROM users WHERE name = "张三";
更多查询规则和方法,见: https://gorm.io/zh_CN/docs/query.html
d. and or 复合查询
参见:Advanced Query | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
db.Where(
db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")),
).Or(
db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"),
).Find(&Pizza{}).Statement
// SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge")
6.5 更新
a. 更新单个字段
// 更新单个字段
func TestUpdateColumn(t *testing.T) {
var result *gorm.DB
// 字符串条件更新
// UPDATE users SET nick_name='张三A', updated_at=当前时间 WHERE nick_name='张三;
result = mysqlClient.Model(&gorme.User{}).Where("nick_name = ?", "张三").
Update("nick_name", "张三A")
fmt.Printf("条件更新: %+v err:%v \n", result.RowsAffected, result.Error)
// 结构体条件更新
// UPDATE users SET age=28, updated_at=当前时间 WHERE member_number='A0001;
result = mysqlClient.Model(&gorme.User{}).Where(&gorme.User{MemberNumber: "A0001"}).Update("age",28)
fmt.Printf("结构体条件更新: %+v err:%v \n", result.RowsAffected, result.Error)
}
/** 输出
=== RUN TestUpdateColumn
条件更新: 1 err:<nil>
结构体条件更新: 1 err:<nil>
--- PASS: TestUpdateColumn (0.06s)
PASS
*/
b. 更新多个字段
// 更新多个字段
func TestUpdateMultipleColumn(t *testing.T) {
var result *gorm.DB
// 使用map
updateMap := map[string]interface{}{
"age": 32,
"birthday": "1991-01-05",
}
// UPDATE users SET age=32,birthday='1991-01-05',updated_at=当前时间 WHERE id=6;
result = mysqlClient.Model(&gorme.User{}).Where("id = ?", 6).Updates(updateMap)
fmt.Printf("使用map结构更新: %+v err:%v \n", result.RowsAffected, result.Error)
// 使用结构体(不使用Select)
updateUser := gorme.User{
Birthday: sql.NullString{String: "1993-10-10", Valid: true},
Age: 0,
}
// @注意这里的age=0不会更新到MySQL
// UPDATE users SET birthday='1993-09-09',updated_at=当前时间 WHERE id=5;
result = mysqlClient.Model(&gorme.User{}).Where("id = ?", 5).Updates(updateUser)
fmt.Printf("使用struct结构更新: %+v err:%v \n", result.RowsAffected, result.Error)
// 使用结构体(使用Select)
updateUser2 := gorme.User{
Birthday: sql.NullString{String: "1993-09-09", Valid: true},
Age: 0,
}
// UPDATE users SET birthday='1993-09-09',age=0,updated_at=当前时间 WHERE id=4;
result = mysqlClient.Model(&gorme.User{}).
Select("birthday", "age"). //指定要更新的字段
Where("id = ?", 4).Updates(updateUser2)
fmt.Printf("使用struct结构更新2: %+v err:%v \n", result.RowsAffected, result.Error)
}
6.6 删除
a. 软删除
// 删除数据
func TestSoftDel(t *testing.T) {
var result *gorm.DB
// 根据主键,删除一条记录
result = mysqlClient.Delete(&gorme.User{},1)
fmt.Printf("根据主键删除一条: %+v err:%v \n", result.RowsAffected, result.Error)
// 根据主键切片,删除多条记录
result = mysqlClient.Delete(&gorme.User{},[]int64{2,3})
fmt.Printf("根据主键切片删除多条: %+v err:%v \n", result.RowsAffected, result.Error)
// 根据条件删除
result = mysqlClient.Where("age = ?",0).Delete(&gorme.User{})
fmt.Printf("根据条件删除: %+v err:%v \n", result.RowsAffected, result.Error)
}
执行上面代码后,发现表里面的记录依然存在,而
DeletedAt
置为当前时间。原因是user
模型中包含了一个gorm.DeletedAt
字段,它将自动获得软删除的能力!
b. 永久删除
可以使用 Unscoped
永久删除匹配的记录。
// 删除数据(硬删除)
func TestStrongDel(t *testing.T) {
var result *gorm.DB
result = mysqlClient.Unscoped().Delete(&gorme.User{}, 1)
fmt.Printf("硬删除: %+v err:%v \n", result.RowsAffected, result.Error)
}
6.7 事务
a. 使用Transaction
// 事务使用(Transaction)
func TestTransaction(t *testing.T) {
err := mysqlClient.Transaction(func(tx *gorm.DB) error {
//在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
// 创建用户记录
user := gorme.User{NickName: "老王", Age: 48}
if err := tx.Create(&user).Error; err != nil {
// 回滚事务
return err
}
// 创建用户地址
userAddress := gorme.UserAddress{Uid: user.ID, Province: "北京", City: "北京", Area: "海淀区"}
if err := tx.Create(&userAddress).Error; err != nil {
// 回滚事务
return err
}
return nil
})
if err != nil {
t.Error(err)
}
fmt.Println("执行完成")
}
b. 手动事务
// 手动事务
func TestUseManualTx(t *testing.T) {
// 用户表
user := gorme.User{NickName: "小丽", Age: 19}
// 开启事务
tx := mysqlClient.Begin()
// 添加用户
if err := tx.Create(&user).Error; err != nil {
// 遇到错误时回滚事务
fmt.Println("添加用户失败: ",err)
tx.Rollback()
}
// 用户地址表
userAddress := gorme.UserAddress{Uid: user.ID, Province: "北京", City: "北京", Area: "昌平区"}
// 添加用户地址
if err := tx.Create(&userAddress).Error; err != nil {
// 遇到错误时回滚事务
fmt.Println("添加用户地址失败: ",err)
tx.Rollback()
}
// 提交事务
tx.Commit()
fmt.Println("执行完成")
}
@注意: 在实践中发现,执行时虽然事务失败,但是主键依然递增。具体看下图效果:
7、gorm 反向生成 struct
db2struct --host rdsfjnifbfjnifbo.mysql.rds.aliyuncs.com -d bigbusiness -t customer --package main --struct customer -p -u bigbusiness
db2struct [-H] [-p] [-v] --package pkgName --struct structName --database databaseName --table tableName
Options:
-H, --host= Host to check mariadb status of
–mysql_port=3306 Specify a port to connect to
-t, --table= Table to build struct from
-d, --database=nil Database to for connection
-u, --user=user user to connect to database
-v, --verbose Enable verbose output
–package= name to set for package
–struct= name to set for struct
–json Add json annotations (default)
–no-json Disable json annotations
–gorm Add gorm annotations (tags)
–guregu Add guregu null types
–target= Save file path
-p, --password= Mysql password
-h, --help Show usage message
–version Show version
8、参考操作
插入
//插入数据
func (user *User) Insert() {
//这里使用了Table()函数,如果你没有指定全局表名禁用复数,或者是表名跟结构体名不一样的时候
//你可以自己在sql中指定表名。这里是示例,本例中这个函数可以去除。
db.Table("user").Create(user)
}
更新
//注意,Model方法必须要和Update方法一起使用
//使用效果相当于Model中设置更新的主键key(如果没有where指定,那么默认更新的key为id),Update中设置更新的值
//如果Model中没有指定id值,且也没有指定where条件,那么将更新全表
//相当于:update user set name='xiaoming' where id=1;
user := User{Id: 1,Name:"xiaoming"}
db.Model(&user).Update(user)
//注意到上面Update中使用了一个Struct,你也可以使用map对象。
//需要注意的是:使用Struct的时候,只会更新Struct中这些非空的字段。
//对于string类型字段的"",int类型字段0,bool类型字段的false都被认为是空白值,不会去更新表
//下面这个更新操作只使用了where条件没有在Model中指定id
//update user set name='xiaohong' wehre sex=1
db.Model(&User{}).Where("sex = ?",1).Update("name","xiaohong")
如果你想手动将某个字段set为空值, 可以使用单独选定某些字段的方式来更新:
user := User{Id: 1}
db.Model(&user).Select("name").Update(map[string]interface{}{"name":"","age":0})
忽略掉某些字段:
当你的更新的参数为结构体,而结构体中某些字段你又不想去更新,那么可以使用Omit方法过滤掉这些不想update到库的字段:
user := User{Id: 1,Name:"xioaming",Age:12}
db.Model(&user).Omit("name").Update(&user)
删除
//delete from user where id=1;
user := User{Id: 1}
db.Delete(&user)
//delete from user where id > 11;
db.Delete(&User{},"id > ?",11)
事务
func CreateAnimals(db *gorm.DB) err {
tx := db.Begin()
// 注意,一旦你在一个事务中,使用tx作为数据库句柄
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
查询:
func (user *User) query() (u []User) {
//查询所有记录
db.Find(&u)
//Find方法可以带 where 参数
db.Find(&u,"id > ? and age > ?",2,12)
//带where 子句的查询,注意where要在find前面
db.Where("id > ?", 2).Find(&u)
// where name in ("xiaoming","xiaohong")
db.Where("name in (?)",[]string{"xiaoming","xiaohong"}).Find(&u)
//获取第一条记录,按照主键顺序排序
db.First(&u)
//First方法可以带where 条件
db.First(&u,"where sex = ?",1)
//获取最后一条记录,按照主键顺序排序
//同样 last方法也可以带where条件
db.Last(&u)
return u
}
注意:方法中带的&u
表示是返回值用u这个对象来接收。
上面的查询都将返回表中所有的字段,如果你想指定查询某些字段该怎么做呢?
指定查询字段-Select
//指定查询字段
db.Select("name,age").Where(map[string]interface{}{"age":12,"sex":1}).Find(&u)
使用Struct和map作为查询条件
//使用Struct,相当于:select * from user where age =12 and sex=1
db.Where(&User{Age:12,Sex:1}).Find(&u)
//等同上一句
db.Where(map[string]interface{}{"age":12,"sex":1}).Find(&u)
not 条件的使用
//where name not in ("xiaoming","xiaohong")
db.Not("name","xiaoming","xiaohong").Find(&u)
//同上
db.Not("name",[]string{"xiaoming","xiaohong"}).Find(&u)
or 的使用
//where age > 12 or sex = 1
db.Where("age > ?",12).Or("sex = ?",1).Find(&u)
order by 的使用
//order by age desc
db.Where("age > ?",12).Or("sex = ?",1).Order("age desc").Find(&u)
limit 的使用
//limit 10
db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Find(&u)
offset 的使用
//limit 300,10
db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Offset(300).Find(&u)
count(*)
//count(*)
var count int
db.Table("user").Where("age > ?",0).Count(&count)
注意:这里你在指定表名的情况下sql为:select count(*) from user where age > 0;
如上代码如果改为:
var count int
var user []User
db.Where("age > ?",0).Find(&user).Count(&count)
相当于你先查出来[]User,然后统计这个list的长度。跟你预期的sql不相符。
group & having
rows, _ := db.Table("user").Select("count(*),sex").Group("sex").
Having("age > ?", 10).Rows()
for rows.Next() {
fmt.Print(rows.Columns())
}
join
db.Table("user u").Select("u.name,u.age").Joins("left join user_ext ue on u.user_id = ue.user_id").Row()
如果有多个连接,用多个Join方法即可。
原生函数
db.Exec("DROP TABLE user;")
db.Exec("UPDATE user SET name=? WHERE id IN (?)", "xiaoming", []int{11,22,33})
db.Exec("select * from user where id > ?",10).Scan(&user)
一些函数
FirstOrInit 和 FirstOrCreate
获取第一个匹配的记录,若没有,则根据条件初始化一个新的记录:
//注意:where条件只能使用Struct或者map。如果这条记录不存在,那么会新增一条name=xiaoming的记录
db.FirstOrInit(&u,User{Name:"xiaoming"})
//同上
db.FirstOrCreate(&u,User{Name:"xiaoming"})
Attrs
如果没有找到记录,则使用Attrs中的数据来初始化一条记录:
//使用attrs来初始化参数,如果未找到数据则使用attrs中的数据来初始化一条
//注意:attrs 必须 要和FirstOrInit 或者 FirstOrCreate 连用
db.Where(User{Name:"xiaoming"}).Attrs(User{Name:"xiaoming",Age:12}).FirstOrInit(&u)
Assign
//不管是否找的到,最终返回结构中都将带上Assign指定的参数
db.Where("age > 12").Assign(User{Name:"xiaoming"}).FirstOrInit(&u)
Pluck
如果user表中你只想查询age这一列,该怎么返回呢,gorm提供了Pluck函数用于查询单列,返回数组:
var ages []int
db.Find(&u).Pluck("age",&ages)
Scan
Scan函数可以将结果转存储到另一个结构体中。
type SubUser struct{
Name string
Age int
}
db.Table("user").Select("name,age").Scan(&SubUser)
sql.Row & sql.Rows
row和rows用户获取查询结果。
//查询一行
row := db.Table("user").Where("name = ?", "xiaoming").Select("name, age").Row() // (*sql.Row)
//获取一行的结果后,调用Scan方法来将返回结果赋值给对象或者结构体
row.Scan(&name, &age)
//查询多行
rows, err := db.Model(&User{}).Where("sex = ?",1).Select("name, age, phone").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
日志#
Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误。
// 启用Logger,显示详细日志
db.LogMode(true)
// 禁用日志记录器,不显示任何日志
db.LogMode(false)
// 调试单个操作,显示此操作的详细日志
db.Debug().Where("name = ?", "xiaoming").First(&User{})
参考文档:
Go ORM 反向生成struct 实例:https://blog.csdn.net/qq_39458593/article/details/106223700
Shelnutt2/db2struct: Converts a mysql table into a golang struct (github.com)
赞助我写出更好的博客