• 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
    • 使用 CreatedAtUpdatedAt 字段追踪创建、更新时间。

    gorm 定义一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt,其他结构体直接匿名嵌套即可。

    4.2 字段标签

    标签名 说明
    column 指定 db 列名
    type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, 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. 单条记录: FirstTakeLast
    • 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语言手册

    Gorm 详细教程

    Go orm框架gorm学习

    Go ORM 概览

    Go ORM 反向生成struct 实例:https://blog.csdn.net/qq_39458593/article/details/106223700

    Shelnutt2/db2struct: Converts a mysql table into a golang struct (github.com)

    Gorm 使用实例

  • 相关阅读:
    win7下的nginx小demo
    破解navicat
    MVC下用C#实现Excel导出
    使用IE10登录,URL出现SessionId的解决办法
    C#错误:The Controls collection cannot be modified
    更改数据库排序规则
    windows server 2008 r2电脑历史操作记录
    jquery easyui无法绑定下拉框内容
    Jquery实现自动提示下拉框
    CLSID {91493441-5A91-11CF-8700-00AA0060263B}错误
  • 原文地址:https://www.cnblogs.com/timelesszhuang/p/gorm.html
Copyright © 2020-2023  润新知