The fantastic ORM library for Golang aims to be developer friendly.。
中文参考地址:https://gorm.io/zh_CN/docs/index.html
GORM具有下面的特性,下面的特性是从中文参考文档中抄录的:
1,全功能 ORM
2,关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
3,Create,Save,Update,Delete,Find 中钩子方法
4,支持 Preload、Joins 的预加载
5,事务,嵌套事务,Save Point,Rollback To Saved Point
6,Context、预编译模式、DryRun 模式
7,批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
8,SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
9,复合主键,索引,约束
10,Auto Migration
11,自定义 Logger
12,灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
13,每个特性都经过了测试的重重考验
14,开发者友好
这里说的模型可以理解成java语言中数据库表对应的实体类,它是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成,例如,下面我们定义一个User的结构体:
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们。
GORM中内置了一个gorm.Model结构体,其包括字段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
}
当然也可以对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "root:root@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接产生错误:", err)
return
}
fmt.Printf("连接成功:%v", db)
}
运行上面代码,可以看到控制台输出:
连接成功:&{0xc0001b6630 <nil> 0 0xc0001c8380 1}
Process finished with exit code 0
**注意:**想要正确的处理 time.Time ,您需要带上 parseTime 参数, (更多参数) 要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4 查看 此文章 获取详情。
MySQL 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
在进行操作之前,先创建一个表user_info,对应的sql语句如下:
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
`mobile_phone` varchar(255) DEFAULT NULL,
`brithday` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
定义UserInfo的结构体:
type UserInfo struct {
ID int32
Name string
Age int32
Address string
MobilePhone string
Brithday time.Time
}
定义一个init方法,该方法用来连接数据库:
var db *gorm.DB
func init() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "root:root@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // Log level
Colorful: true, // 禁用彩色打印
},
)
// 全局模式
//NamingStrategy和Tablename不能同时配置,
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 使用单数表名
},
Logger: newLogger,
})
if err != nil {
fmt.Println("连接产生错误:", err)
return
}
fmt.Printf("连接成功:%v", db)
}
编写一个插入数据的例子,代码实例如下:
func create() {
userInfo := UserInfo{
Name: "张三",
Age: 10,
Address: "上海市",
MobilePhone: "1333333333",
Brithday: time.Now(),
}
result := db.Create(&userInfo)
fmt.Printf("还回主键:%d,影像条数:%d,错误信息:%s", userInfo.ID, result.RowsAffected, result.Error)
}
运行代码,可以看到控制台输出:
[75.403ms] [rows:1] INSERT INTO `user_info` (`name`,`age`,`address`,`mobile_phone`,`brithday`) VALUES ('张三',10,'上海市','1333333333','2022-08-11 09:42:41.875')
还回主键:2,影像条数:1,错误信息:%!s(<nil>)
用给定的字段创建数据:
func createByGiven() {
userInfo := UserInfo{
Name: "张三",
Age: 10,
Address: "上海市",
MobilePhone: "1333333333",
Brithday: time.Now(),
}
//指定"Name", "Age", "Address"三个字段创建数据
result := db.Select("Name", "Age", "Address").Create(&userInfo)
fmt.Printf("还回主键:%d,影像条数:%d,错误信息:%s", userInfo.ID, result.RowsAffected, result.Error)
}
实际执行的sql语句:
INSERT INTO `user_info` (`name`,`age`,`address`) VALUES ('张三',10,'上海市')
忽略给定字段创建数据:
func createOmitByGiven() {
userInfo := UserInfo{
Name: "张三",
Age: 10,
Address: "上海市",
MobilePhone: "1333333333",
Brithday: time.Now(),
}
result := db.Omit("Name", "Age", "Address").Create(&userInfo)
fmt.Printf("还回主键:%d,影像条数:%d,错误信息:%s", userInfo.ID, result.RowsAffected, result.Error)
}
INSERT INTO `user_info` (`mobile_phone`,`brithday`) VALUES ('1333333333','2022-08-11 10:13:49.442')
要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
/**
* 批量插入数据
*/
func createInBatch1() {
userInfos := []UserInfo{{Name: "王五", Brithday: time.Now()}, {Name: "赵六", Brithday: time.Now()}, {Name: "吴七", Brithday: time.Now()}}
db.Create(&userInfos)
for _, user := range userInfos {
fmt.Println(user.ID) // 1,2,3
}
}
该代码执行的语句是:
INSERT INTO `user_info` (`name`,`age`,`address`,`mobile_phone`,`brithday`) VALUES ('王五',0,'','','2022-08-11 22:07:20.222'),('赵六',0,'','','2022-08-11 22:07:20.222'),('吴七',0,'','','2022-08-11 22:07:20.222')
由于sql在插入的时候有长度的限制,对于特别多的数据,可以采用分批次的方式进行插入:
/**
* 批量插入数据
*/
func createInBatch2() {
userInfos := []UserInfo{{Name: "王五", Brithday: time.Now()}, {Name: "赵六", Brithday: time.Now()}, {Name: "吴七", Brithday: time.Now()}}
//一次性插入两条数据
db.CreateInBatches(userInfos, 2)
for _, user := range userInfos {
fmt.Println(user.ID) // 1,2,3
}
}
该代码对应执行语句是,gorm会将三条语句分两批次执行掉:
INSERT INTO `user_info` (`name`,`age`,`address`,`mobile_phone`,`brithday`) VALUES ('王五',0,'','','2022-08-11 22:11:50.032'),('赵六',0,'','','2022-08-11 22:11:50.032')
INSERT INTO `user_info` (`name`,`age`,`address`,`mobile_phone`,`brithday`) VALUES ('吴七',0,'','','2022-08-11 22:11:50.032')
GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误。
例子一:
func selectOne() {
var userInfo UserInfo
tx := db.First(&userInfo)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo)
var userInfo1 UserInfo
tx = db.Take(&userInfo1)
//SELECT * FROM `user_info` LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo1)
var userInfo2 UserInfo
tx = db.Last(&userInfo2)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` DESC LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo2)
}
通过上面例子可以看出,First方法查出来的是通过id升序取第一条数据,Take方法是取一条数据,Last方法是通过id降序取一条数据。在这里需要注意的是,在三个查询方法中传入的都是一个空的结构体,如果传入的结构体有id,查询会带上id在sql中,通过下面的例子可以看出,由于第一条查询语句查出来了数据,所以后面两条查询都带上了该条数据的id:
func selectOne1() {
var userInfo UserInfo
tx := db.First(&userInfo)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo)
tx = db.Take(&userInfo)
//SELECT * FROM `user_info` WHERE `user_info`.`id` = 1 LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo)
tx = db.Last(&userInfo)
//SELECT * FROM `user_info` WHERE `user_info`.`id` = 1 ORDER BY `user_info`.`id` DESC LIMIT 1
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo)
}
上面的三个方法在查询的过程中,如果没有查到数据,会输出ErrRecordNotFound的错误,通过下面的例子可以演示一下,该处代码输入的是一个数据库中不存在的ID。
func selectOneErr() {
userInfo := UserInfo{ID: 1000}
tx := db.First(&userInfo)
fmt.Printf("影响条数:%d,错误信息:%v,查询到的数据:%v", tx.RowsAffected, tx.Error, userInfo)
if tx.Error != nil {
is := errors.Is(tx.Error, gorm.ErrRecordNotFound)
fmt.Println("是否是错误:gorm.ErrRecordNotFound,", is)
}
}
控制台输出:
[26.063ms] [rows:0] SELECT * FROM `user_info` WHERE `user_info`.`id` = 1000 ORDER BY `user_info`.`id` LIMIT 1
影响条数:0,错误信息:record not found,查询到的数据:{1000 0 0001-01-01 00:00:00 +0000 UTC}是否是错误:gorm.ErrRecordNotFound, true
例子二:
First 和 Last 会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。 例如:
func selectOneWith() {
var userInfo UserInfo
db.First(&userInfo)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` LIMIT 1
result := map[string]interface{}{}
db.Model(&UserInfo{}).First(&result)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` LIMIT 1
//此语句会查询报错
result1 := map[string]interface{}{}
db.Table("user_info").First(&result1)
//2022/08/13 14:40:09 D:/code/other/go-project/src/project-study/gorm-study-01/ch03/main.go:121 model value required
//[9.788ms] [rows:0] SELECT * FROM `user_info` ORDER BY `user_info`. LIMIT 1
result2 := map[string]interface{}{}
db.Table("user_info").Take(&result2)
// SELECT * FROM `user_info` LIMIT 1
}
如果主键是数字类型,您可以使用 内联条件 来检索对象。 传入字符串参数时,需要特别注意 SQL 注入问题,查看 安全 获取详情。
func selectByPrimary() {
var userInfo UserInfo
// 查询ID为10的数据
db.First(&userInfo, 10)
//SELECT * FROM `user_info` WHERE `user_info`.`id` = 10 ORDER BY `user_info`.`id` LIMIT 1
// 查询ID为10的数据(这里的入参是字符串)
var userInfo1 UserInfo
db.First(&userInfo1, "10")
//SELECT * FROM `user_info` WHERE `user_info`.`id` = '10' ORDER BY `user_info`.`id` LIMIT 1
//查询ID在1,2,3中
var userInfo2 UserInfo
db.Find(&userInfo2, []int{1, 2, 3})
//SELECT * FROM `user_info` WHERE `user_info`.`id` IN (1,2,3)
//如果id是字符串可以按照这个方式查询
var userInfo3 UserInfo
db.First(&userInfo3, "id = ?", "20220813000001")
//SELECT * FROM `user_info` WHERE id = '20220813000001' ORDER BY `user_info`.`id` LIMIT 1
//可以在结构体赋值查询
var user = UserInfo{ID: 10}
db.First(&user)
// SELECT * FROM `user_info` WHERE `user_info`.`id` = 10 ORDER BY `user_info`.`id` LIMIT 1
//通过Modal查询
var result UserInfo
db.Model(UserInfo{ID: 10}).First(&result)
//SELECT * FROM `user_info` ORDER BY `user_info`.`id` LIMIT 1
}
可以使用Find方法查询所有数据:
func selectAll() {
var userInfos []UserInfo
result := db.Find(&userInfos)
//[69.638ms] [rows:11] SELECT * FROM `user_info`
//查询总条数:11,错误信息:
fmt.Printf("查询总条数:%d,错误信息:%v", result.RowsAffected, result.Error)
}
func selectByConditions() {
var userInfo UserInfo
var userInfos UserInfo
// 查询第一条满足条件额度记录
db.Where("name = ?", "王五").First(&userInfo)
// SELECT * FROM `user_info` WHERE name = '王五' ORDER BY `user_info`.`id` LIMIT 1
// 查询所有满足条件的记录
db.Where("name <> ?", "王五").Find(&userInfos)
//SELECT * FROM `user_info` WHERE name <> '王五'
var userInfos1 UserInfo
// IN
db.Where("name IN ?", []string{"王五", "赵六"}).Find(&userInfos1)
// SELECT * FROM `user_info` WHERE name IN ('王五','赵六')
var userInfos2 UserInfo
// LIKE
db.Where("name LIKE ?", "%王%").Find(&userInfos2)
// SELECT * FROM `user_info` WHERE name LIKE '%王%'
var userInfos3 UserInfo
// AND
db.Where("name = ? AND age >= ?", "王五", "16").Find(&userInfos3)
// SELECT * FROM `user_info` WHERE name = '王五' AND age >= '16'
var userInfos4 UserInfo
// Time
db.Where("brithday > ?", "2001-08-08 00:00:00").Find(&userInfos4)
// SELECT * FROM `user_info` WHERE brithday > '2001-08-08 00:00:00'
var userInfos5 UserInfo
// BETWEEN
db.Where("brithday BETWEEN ? AND ?", "2001-08-08 00:00:00", "2021-08-08 00:00:00").Find(&userInfos5)
// SELECT * FROM `user_info` WHERE brithday BETWEEN '2001-08-08 00:00:00' AND '2021-08-08 00:00:00'
}
func selectByMapOrStruct() {
var userInfo UserInfo
// Struct
db.Where(&UserInfo{Name: "张三", Age: 20}).First(&userInfo)
// SELECT * FROM `user_info` WHERE `user_info`.`name` = '张三' AND `user_info`.`age` = 20 ORDER BY `user_info`.`id` LIMIT 1
var userInfos []UserInfo
// Map
db.Where(map[string]interface{}{"name": "张三", "age": 20}).Find(&userInfos)
// SELECT * FROM `user_info` WHERE `age` = 20 AND `name` = '张三'
var userInfos1 []UserInfo
// Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&userInfos1)
// SELECT * FROM `user_info` WHERE `user_info`.`id` IN (20,21,22)
var userInfos2 []UserInfo
db.Where(&UserInfo{Name: "张三", Age: 0}).Find(&userInfos2)
//SELECT * FROM `user_info` WHERE `user_info`.`name` = '张三'
var userInfos3 []UserInfo
db.Where(map[string]interface{}{"Name": "张三", "Age": 0}).Find(&userInfos3)
//SELECT * FROM `user_info` WHERE `Age` = 0 AND `Name` = '张三'
}
注意在使用结构体查询的时候,只会将非零的条件参数组装到sql中去,这就意味着0,false,''将不会组装到sql中去。上面的倒数第二条就是这个例子,如果要加上零条件,可以使用map入参,如最后一个例子。
在使用结构体进行搜索时,可以通过将相关字段名传递到查询条件中来指定在查询条件中使用结构中的哪些特定值。
func selectByZhiding() {
var userInfos []UserInfo
//指定了name和age字段,但是age没有赋值,所有查询条件是`name` = '张三' `age` = 0
db.Where(&UserInfo{Name: "张三"}, "name", "Age").Find(&userInfos)
// SELECT * FROM `user_info` WHERE `user_info`.`name` = '张三' AND `user_info`.`age` = 0
var userInfos1 []UserInfo
//只指定了age字段,但是age字段没有赋值,所以查询条件是age=0
db.Where(&UserInfo{Name: "张三"}, "Age").Find(&userInfos1)
// SELECT * FROM `user_info` WHERE `user_info`.`age` = 0
}
查询条件的使用方式可以像where一样内联到方法First和Find中使用
func selectInline() {
var userInfo UserInfo
// Get by primary key if it were a non-integer type
db.First(&userInfo, "id = ?", "1")
// SELECT * FROM `user_info` WHERE id = '1' ORDER BY `user_info`.`id` LIMIT 1
var userInfo1 UserInfo
// Plain SQL
db.Find(&userInfo1, "name = ?", "张三")
// SELECT * FROM `user_info` WHERE name = '张三'
var userInfos UserInfo
db.Find(&userInfos, "name <> ? AND age > ?", "张三", 20)
// SELECT * FROM `user_info` WHERE name <> '张三' AND age > 20
var userInfos1 UserInfo
// Struct
db.Find(&userInfos1, UserInfo{Age: 20})
// SELECT * FROM `user_info` WHERE `user_info`.`age` = 20
var userInfos2 UserInfo
// Map
db.Find(&userInfos2, map[string]interface{}{"age": 20})
// SELECT * FROM `user_info` WHERE `age` = 20
}
Not的使用方法和where相似
func selectNot() {
var userInfo UserInfo
db.Not("name = ?", "张三").First(&userInfo)
// SELECT * FROM `user_info` WHERE NOT name = '张三' ORDER BY `user_info`.`id` LIMIT 1
var userInfos []UserInfo
// Not In
db.Not(map[string]interface{}{"name": []string{"张三", "李四"}}).Find(&userInfos)
// SELECT * FROM `user_info` WHERE `name` NOT IN ('张三','李四')
var userInfos1 []UserInfo
// Struct
db.Not(UserInfo{Name: "张三", Age: 18}).First(&userInfos1)
// SELECT * FROM `user_info` WHERE (`user_info`.`name` <> '张三' AND `user_info`.`age` <> 18) ORDER BY `user_info`.`id` LIMIT 1
var userInfos2 []UserInfo
// Not In slice of primary keys
db.Not([]int64{1, 2, 3}).First(&userInfos2)
// SELECT * FROM `user_info` WHERE `user_info`.`id` NOT IN (1,2,3) ORDER BY `user_info`.`id` LIMIT 1
}
func selectOr() {
var userInfos []UserInfo
db.Where("name = ?", "张三").Or("name = ?", "李四").Find(&userInfos)
// SELECT * FROM `user_info` WHERE name = '张三' OR name = '李四'
var userInfos1 []UserInfo
// Struct
db.Where("name = '张三'").Or(UserInfo{Name: "李四", Age: 18}).Find(&userInfos1)
// SELECT * FROM `user_info` WHERE name = '张三' OR (`user_info`.`name` = '李四' AND `user_info`.`age` = 18)
var userInfos2 []UserInfo
// Map
db.Where("name = '张三'").Or(map[string]interface{}{"name": "李四", "age": 18}).Find(&userInfos2)
// SELECT * FROM `user_info` WHERE name = '张三' OR (`age` = 18 AND `name` = '李四')
}
Select允许您指定要从数据库检索的字段。否则,GORM将默认选择所有字段。
func selectZdzd() {
var userInfos []UserInfo
db.Select("name", "age").Find(&userInfos)
// SELECT `name`,`age` FROM `user_info`
var userInfos1 []UserInfo
db.Select([]string{"name", "age"}).Find(&userInfos1)
// SELECT `name`,`age` FROM `user_info`
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,42) FROM `users`
}
通过指定字段排序从数据库中读取数据
func selectOrder() {
var userInfos []UserInfo
db.Order("age desc, name").Find(&userInfos)
// SELECT * FROM `user_info` ORDER BY age desc, name
var userInfos1 []UserInfo
// Multiple orders
db.Order("age desc").Order("name").Find(&userInfos1)
// SELECT * FROM `user_info` ORDER BY age desc,name
}
限制:指定要检索的最大记录数。偏移量:指定开始返回记录之前要跳过的记录数
func selectLimit() {
var userInfos []UserInfo
db.Limit(3).Find(&userInfos)
// SELECT * FROM `user_info` LIMIT 3
var userInfo1 UserInfo
var userInfos1 []UserInfo
// Cancel limit condition with -1
db.Limit(10).Find(&userInfo1).Limit(-1).Find(&userInfos1)
// SELECT * FROM `user_info` LIMIT 10; (users1)
// SELECT * FROM `user_info` ; (users2)
var userInfos3 []UserInfo
//分页查询
db.Limit(10).Offset(5).Find(&userInfos3)
// SELECT * FROM `user_info` LIMIT 10 OFFSET 5
}
func selectGroupByOrHaving() {
var result Result
db.Model(&UserInfo{}).Select("name, sum(age) as total").Where("name LIKE ?", "张%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `user_info` WHERE name LIKE '张%' GROUP BY `name` ORDER BY `user_info`.`id` LIMIT 1
fmt.Println(result)
db.Model(&UserInfo{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "王五").Find(&result)
// SELECT name, sum(age) as total FROM `user_info` GROUP BY `name` HAVING name = '王五'
fmt.Println(result)
rows, _ := db.Table("user_info").Select("date(brithday) as date, sum(age) as total").Group("date(brithday)").Rows()
//SELECT date(brithday) as date, sum(age) as total FROM `user_info` GROUP BY date(brithday)
defer rows.Close()
fmt.Println(rows)
for rows.Next() {
var s Result
db.ScanRows(rows, &s)
fmt.Printf("%v", s)
}
fmt.Println("------------------------->>>>")
rows, _ = db.Table("user_info").Select("date(brithday) as date, sum(age) as total").Group("date(brithday)").Having("sum(age) > ?", 10).Rows()
//SELECT date(brithday) as date, sum(age) as total FROM `user_info` GROUP BY date(brithday) HAVING sum(age) > 10
defer rows.Close()
for rows.Next() {
strings, _ := rows.Columns()
fmt.Printf("%v", strings)
}
var results []Result
db.Table("user_info").Select("date(brithday) as date, sum(age) as total").Group("date(brithday)").Having("sum(age) > ?", 10).Scan(&results)
//SELECT date(brithday) as date, sum(age) as total FROM `user_info` GROUP BY date(brithday) HAVING sum(age) > 10
fmt.Println(results)
}
func selectDistinct() {
var results []UserInfo
db.Distinct("name", "age").Order("name, age desc").Find(&results)
//
fmt.Println(results)
}
在演示joins之前,先创建表base_info
CREATE TABLE `base_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`nick_name` varchar(255) DEFAULT NULL,
`home_address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
下面是joins的使用方式
func selectJoins() {
var results []BaseResult
db.Model(&UserInfo{}).Select("user_info.name, base_info.nick_name").Joins("left join base_info on base_info.uid = user_info.id").Scan(&results)
//SELECT user_info.name, base_info.nick_name FROM `user_info` left join base_info on base_info.uid = user_info.id
fmt.Println(results)
rows, _ := db.Table("user_info").Select("user_info.name, base_info.nick_name").Joins("left join base_info on base_info.uid = user_info.id").Rows()
//SELECT user_info.name, base_info.nick_name FROM `user_info` left join base_info on base_info.uid = user_info.id
for rows.Next() {
var s Result
db.ScanRows(rows, &s)
fmt.Println(s)
}
var res []Result
db.Table("user_info").Select("user_info.name, base_info.nick_name").Joins("left join base_info on base_info.uid = user_info.id").Scan(&res)
//SELECT user_info.name, base_info.nick_name FROM `user_info` left join base_info on base_info.uid = user_info.id
fmt.Println(res)
var ress []UserInfo
db.Joins("JOIN base_info on base_info.uid = user_info.id and base_info.nick_name= ?", "zhangsan").Where("user_info.name = ?", "张三").Find(&ress)
//SELECT `user_info`.`id`,`user_info`.`name`,`user_info`.`age`,`user_info`.`address`,`user_info`.`mobile_phone`,`user_info`.`brithday` FROM `user_info` JOIN base_info on base_info.uid = user_info.id and base_info.nick_name= 'zhangsan' WHERE user_info.name = '张三'
fmt.Println(ress)
}
查询的条数和传入的接受参数类型有关
func selectScan() {
//查询出单条数据
var result Result
db.Table("user_info").Select("name", "age").Where("name = ?", "张三").Scan(&result)
//SELECT name,age FROM `user_info` WHERE name = '张三'
// Raw SQL
db.Raw("SELECT name, age FROM user_info WHERE name = ?", "王五").Scan(&result)
//SELECT name, age FROM user_info WHERE name = '王五'
//查询出多条数据(和传入的接受参数有关)
var results []Result
db.Table("user_info").Select("name", "age").Where("name = ?", "张三").Scan(&results)
db.Raw("SELECT name, age FROM user_info WHERE name = ?", "王五").Scan(&results)
}