• GORM使用指南一



    ​ gorm是一款开源的用来操作数据库的工具,采用提供的依赖包可以很方便的对数据库进行操作,正如官网描述的一下 The fantastic ORM library for Golang aims to be developer friendly.

    ​ 中文参考地址:https://gorm.io/zh_CN/docs/index.html

    简介

    1.1 特性

    ​ GORM具有下面的特性,下面的特性是从中文参考文档中抄录的:

    ​ 1,全功能 ORM

    ​ 2,关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)

    ​ 3,Create,Save,Update,Delete,Find 中钩子方法

    ​ 4,支持 PreloadJoins 的预加载

    ​ 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,开发者友好

    1.2 模型定义

    ​ 这里说的模型可以理解成java语言中数据库表对应的实体类,它是标准的 struct,由 Go 的基本数据类型、实现了 ScannerValuer 接口的自定义类型及其指针或别名组成,例如,下面我们定义一个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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ​ GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们

    ​ GORM中内置了一个gorm.Model结构体,其包括字段IDCreatedAtUpdatedAtDeletedAt。

    // gorm.Model 的定义
    type Model struct {
      ID        uint           `gorm:"primaryKey"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt gorm.DeletedAt `gorm:"index"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ​ 在实际的使用过程中,我们可以作为内嵌用来使用,通过下面的例子可以理解:

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ 当然也可以对于正常的结构体字段,你也可以通过标签 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.3 连接mysql

    ​ 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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ​ 运行上面代码,可以看到控制台输出:

    连接成功:&{0xc0001b6630 <nil> 0 0xc0001c8380 1}
    Process finished with exit code 0
    
    • 1
    • 2

    ​ **注意:**想要正确的处理 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{})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2 CURD操作

    ​ 在进行操作之前,先创建一个表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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ​ 定义UserInfo的结构体:

    type UserInfo struct {
    	ID          int32
    	Name        string
    	Age         int32
    	Address     string
    	MobilePhone string
    	Brithday    time.Time
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ​ 定义一个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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2.1 新增

    2.1.1 创建记录

    ​ 编写一个插入数据的例子,代码实例如下:

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 运行代码,可以看到控制台输出:

    [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>)
    
    • 1
    • 2

    2.1.2 用指定字段创建记录

    ​ 用给定的字段创建数据:

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 实际执行的sql语句:

    INSERT INTO `user_info` (`name`,`age`,`address`) VALUES ('张三',10,'上海市')
    
    • 1

    ​ 忽略给定字段创建数据:

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    INSERT INTO `user_info` (`mobile_phone`,`brithday`) VALUES ('1333333333','2022-08-11 10:13:49.442')
    
    • 1

    2.1.3 批量插入

    ​ 要有效地插入大量记录,请将一个 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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ 该代码执行的语句是:

    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')
    
    • 1

    ​ 由于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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ 该代码对应执行语句是,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')
    
    • 1
    • 2
    • 3

    3 查询

    3.1 查询一条记录

    ​ GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 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)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ​ 通过上面例子可以看出,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)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    ​ 上面的三个方法在查询的过程中,如果没有查到数据,会输出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)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ 控制台输出:

    [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
    
    • 1
    • 2

    例子二:

    FirstLast 会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 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
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.2 主键检索

    ​ 如果主键是数字类型,您可以使用 内联条件 来检索对象。 传入字符串参数时,需要特别注意 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.3 查询全部对象

    ​ 可以使用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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.4 条件查询

    例子一 通过string条件查询,下面例子是入参是string进行查询

    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'
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    例子二 结构体或者map进行查询:

    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` = '张三'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    ​ 注意在使用结构体查询的时候,只会将非零的条件参数组装到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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    例子四 内联条件

    ​ 查询条件的使用方式可以像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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    例子五 Not条件

    ​ 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
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    例子六 Or条件

    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` = '李四')
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    例子七 选择指定字段查询

    ​ 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`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    例子八 ORDER查询

    ​ 通过指定字段排序从数据库中读取数据

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    例子九 Limit和Offset实现分页

    ​ 限制:指定要检索的最大记录数。偏移量:指定开始返回记录之前要跳过的记录数

    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
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    例子十 Group by 和 Having

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    例子十一 Distinct

    func selectDistinct() {
    
    	var results []UserInfo
    	db.Distinct("name", "age").Order("name, age desc").Find(&results)
    	//
    	fmt.Println(results)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例子十二 Joins

    ​ 在演示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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ​ 下面是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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    例子十三 Sacn

    ​ 查询的条数和传入的接受参数类型有关

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    我国农业科学数据共享协议
    什么是系统架构?
    如何使用自定义Promptbooks优化您的安全工作流程
    架构师日常(一)
    四、固态硬盘存储技术的分析(论文)
    马来西亚农村致富经 丰收节贸促会-艾迪:跨境电商中国样本
    浏览器工作原理
    测试报告 拍卖系统
    centos的环境配置
    在移动硬盘上安装Win11系统(不使用工具)
  • 原文地址:https://blog.csdn.net/qq_36305027/article/details/126332224