G

[Golang]Gorm框架学习日志

RoLingG 2023-11-07

简单连接

后面的代码都要基于这个简单连接的基础上进行编写。

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面,不用可以注释掉
      NamingStrategy: schema.NamingStrategy{
         //TablePrefix:   "f_", // 表名前缀
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
   //fmt.Println(db)
}

// gorm采用的命名策略是,表名是蛇形复数,字段名是蛇形单数
type Student struct {
   ID   uint
   Name string
   Age  int
}

func main() {
   //完成表的创建
   DB.AutoMigrate(&Student{})
   fmt.Println(DB)
}

显示日志

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
    username := "root1"    //账号
    password := "rootroot" //密码
    host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
    port := 3306           //数据库端口
    DBname := "GoORM"      //数据库名
    timeout := "10s"       //连接超时,10秒

    //①加全局info,根据环境不同也可以显示不同日志,例如把info换成error,只看报错那些,但这样显示日志有点占用空间
    //var mysqlLogger logger.Interface
    mysqlLogger = logger.Default.LogMode(logger.Info)

    // root:root@tcp(127.0.0.1:3306)/gorm?
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
    //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
        //SkipDefaultTransaction: true,

        //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
        NamingStrategy: schema.NamingStrategy{
            //TablePrefix:   "f_", // 表名前缀
            SingularTable: false, // 是否单数表名
            NoLowerCase:   true,  // 是否关闭小写转换
        },

        //Logger: mysqlLogger,
    })
    if err != nil {
        panic("连接数据库失败, error=" + err.Error())
    }
    DB = db
    // 连接成功
    //fmt.Println(db)
}

// gorm采用的命名策略是,表名是蛇形复数,字段名是蛇形单数
type Student struct {
    ID   uint
    Name string
    Age  int
}

func main() {
    //②部分展示日志,这样相对来说就没那么占用空间
    //DB = DB.Session(&gorm.Session{
    //    Logger: mysqlLogger,
    //})

    //完成表的创建,如果本身就有表,那么就不会再创建。
    //DB.AutoMigrate(&Student{})

    //③用Debug,这样就能局部让某个操作有日志。Debug本质就是创建了一个Session,把logger给放了进来
    DB.Debug().AutoMigrate(&Student{})
    fmt.Println(DB)
}

模型定义

定义一个表也就是定义一个struct,例如:

type Student struct {
   ID   uint
   Name string
   Age  int
}

小写属性是不会生成字段的。

自动生成表结构

DB.AutoMigrate(&Student{})

AutoMigrate的逻辑是只新增,不删除,不修改。

意味着如果struct里把Name去掉换成Name1,运行之后会发现Name1Name都在,这就是只新增而不删除,不修改。

而且AutoMigrate生成字段的类型都是大类型

所以要在struct里用gorm里的size标签(但是表的主键可能会改不了,大概是因为数据库本身设计的问题。)

改完之后:

type Student struct {
   ID    uint    `gorm:"size:16"`
   Name  string  `gorm:"size:16"` 
   Age   int     `gorm:"size:3"`
   Email *string `gorm:"size:128"`
}

//也可以自己定义类型,符合sql语句就行。
type Student struct {
    ID    uint    `gorm:"size:16"`
    Name  string  `gorm:"type:varchar(16)"` 
    Age   int     `gorm:"size:3"`
    Email *string `gorm:"size:128"` 
}

常用字段标签:

type 定义字段类型

size 定义字段大小

column 自定义列名

primaryKey 将列定义为主键

unique 将列定义为唯一键

default 定义列的默认值

not null 不可为空

embedded 嵌套字段

embeddedPrefix 嵌套字段前缀

comment 注释

多个标签之前用 ; 连接

使用案例:

type Student struct {
  Name string      `gorm:"type:varchar(12);not null;comment:用户名"`
  UUID string      `gorm:"primaryKey;unique;comment:主键"`
  Info StudentInfo `gorm:"embedded;embeddedPrefix:s_"`
}

单表查询

添加记录:

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
    username := "root1"    //账号
    password := "rootroot" //密码
    host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
    port := 3306           //数据库端口
    DBname := "GoORM"      //数据库名
    timeout := "10s"       //连接超时,10秒

    mysqlLogger = logger.Default.LogMode(logger.Info)

    // root:root@tcp(127.0.0.1:3306)/gorm?
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
    //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
        //SkipDefaultTransaction: true,

        //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false, // 是否单数表名
            NoLowerCase:   true,  // 是否关闭小写转换
        },
    })
    if err != nil {
        panic("连接数据库失败, error=" + err.Error())
    }
    DB = db
    // 连接成功
}

type Student struct {
    ID     uint   `gorm:"size:3"`
    Name   string `gorm:"size:8"`
    Age    int    `gorm:"size:3"`
    Gender bool
    Email  *string `gorm:"size:32"`
}

func main() {
    //DB.AutoMigrate(&Student{})
    DB = DB.Session(&gorm.Session{
        Logger: mysqlLogger,
    })
    email := "rolingg@qq.com"
    //添加记录
    s1 := Student{
        //其实ID也在。
        Name:   "RoLingG",
        Age:    21,
        Gender: true,
        Email:  &email,
    }
    err := DB.Create(&s1).Error

    fmt.Println(err)
}

指针类型是为了更好的存储null类型,但传值的时候还是一样,就是要记得传指针。

Create接收的是一个指针,不是一个值

由于传的是一个指针,调用完create之后,st。udent这个对象上就有该记录的信息了,例如上述的ID。

批量插入

//批量插入
var studentList []Student
for i := 0; i < 10; i++ {
   studentList = append(studentList, Student{
      Name:   fmt.Sprintf("RoLingG%d", i+1),
      Age:    21 + i + 1,
      Gender: true,
      Email:  nil,
   })
}
err := DB.Create(&studentList).Error

fmt.Println(err)

单条记录查询

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3"`
   Name   string `gorm:"size:12"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func main() {
   DB.AutoMigrate(&Student{})
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })
   
   var student Student
   //无顺序,默认按照ID查询
   DB = DB.Session(&gorm.Session{Logger: mysqlLogger})
   DB.Take(&student)
   fmt.Println(student)
   //直接这样写会和上面的student冲突,因为上面的student里已经有数据了
   //DB.First(&student)
   //fmt.Println(student)

   //所以要重新赋值
   student = Student{}
   DB.First(&student)
   fmt.Println(student)
   student = Student{}
   DB.Last(&student)
   fmt.Println(student)

    //根据主键自定义查询
    student = Student{}
    //将主键ID为2的拿出来查询
    DB.Take(&student, 2)
    //err := DB.Take(&student, 2).Error
    //err := DB.Take(&student, 45).Error
    fmt.Println(student)
    //gorm.ErrRecordNotFound:一个bool值,用来看记录是否查询失败
    //fmt.Println(err == gorm.ErrRecordNotFound)
    //如果有错,则student返回的全是默认值,且会有error日志
    //fmt.Println(err)

    //根据name查询
    student = Student{}
    //用?进行拼接,尽量别用fmt.Sprintf()进行拼接,很容易造成查询失误导致数据泄露,用?作为占位符能有效的方式sql注入,?能进行转译限制。
    DB.Take(&student, "Name = ?", "RoLingG9")
    fmt.Println(student)

    //根据struct查询
    student = Student{}
    //只能根据主要值查询,也就是主键
    student.ID = 2
    DB.Take(&student)
    fmt.Println(student)
}

查询多条记录

切片即可

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
    username := "root1"    //账号
    password := "rootroot" //密码
    host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
    port := 3306           //数据库端口
    DBname := "GoORM"      //数据库名
    timeout := "10s"       //连接超时,10秒

    mysqlLogger = logger.Default.LogMode(logger.Info)

    // root:root@tcp(127.0.0.1:3306)/gorm?
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
    //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
        //SkipDefaultTransaction: true,

        //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false, // 是否单数表名
            NoLowerCase:   true,  // 是否关闭小写转换
        },
    })
    if err != nil {
        panic("连接数据库失败, error=" + err.Error())
    }
    DB = db
    // 连接成功
}

type Student struct {
    ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
    Name   string `gorm:"size:12" json:"name"`
    Age    int    `gorm:"size:3"`
    Gender bool
    Email  *string `gorm:"size:32"`
}

func main() {
    DB.AutoMigrate(&Student{})
    DB = DB.Session(&gorm.Session{
        Logger: mysqlLogger,
    })
    //查询多条数据
    var studentList []Student
    count := DB.Find(&studentList)
    fmt.Println(count)
    for _, student := range studentList {
        fmt.Println(student)
    }
    //json的形式
    //json形式可以看到指针转译的值,例如这里的邮箱,普通是输出是看的是机器码,而json输出看的是实际值
    data, _ := json.Marshal(studentList)
    fmt.Println(string(data))
}

根据id查询、name查询:

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
    username := "root1"    //账号
    password := "rootroot" //密码
    host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
    port := 3306           //数据库端口
    DBname := "GoORM"      //数据库名
    timeout := "10s"       //连接超时,10秒

    mysqlLogger = logger.Default.LogMode(logger.Info)

    // root:root@tcp(127.0.0.1:3306)/gorm?
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
    //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
        //SkipDefaultTransaction: true,

        //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false, // 是否单数表名
            NoLowerCase:   true,  // 是否关闭小写转换
        },
    })
    if err != nil {
        panic("连接数据库失败, error=" + err.Error())
    }
    DB = db
    // 连接成功
}

type Student struct {
    ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
    Name   string `gorm:"size:12" json:"name"`
    Age    int    `gorm:"size:3"`
    Gender bool
    Email  *string `gorm:"size:32"`
}

func main() {
    DB.AutoMigrate(&Student{})
    DB = DB.Session(&gorm.Session{
        Logger: mysqlLogger,
    })

    //查询多条数据
    var studentList []Student
    count := DB.Find(&studentList)
    fmt.Println(count)
    for _, student := range studentList {
        fmt.Println(student)
    }
    //根据id列表查
    DB.Find(&studentList, []int{4, 7, 9})
    fmt.Println(studentList)
    //根据name查
    DB.Find(&studentList, "name in (?)", []string{"RoLingG1", "RoLingG2", "RoLingG8"})
    fmt.Println(studentList)
}

各类操作

更新操作

save保存所有字段

用于单个记录(数据)的全字段更新

会保存所有字段,即使是零值也会保存

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func main() {
   DB.AutoMigrate(&Student{})
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })
   
   //save更新
   var student Student
   DB.Take(&student, 10)
   student.Email = nil
   student.Age = 0
   student.Name = "Clouwer"
   DB.Save(&student)
   //限定某个字段进行更新
   student.Email = nil
   student.Age = 31 //Age字段为0,并没有改成31
   student.Name = "RoLingG"
   DB.Select("Name").Save(&student)
}
update/updates更新多个记录

常用于批量更新,以及更新多列

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func main() {
   DB.AutoMigrate(&Student{})
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //update更新
   var studentList []Student
   //DB.Find(&studentList, []int{7, 8, 9}).Update("Email", "11223344@qq.com")
   //updates更新多列、多字段(updates传的是结构体时,更新为零值是不会更改的。而传的map时,是可以更改的)
   //用结构体进行数据传输
   //DB.Find(&studentList, []int{7, 8, 9}).Updates(Student{
   // Name:   "Clouwer",
   // Age:    19,
   // Gender: false,
   //})
   //用map进行数据传输
   DB.Find(&studentList, []int{7, 8, 9}).Updates(map[string]any{
      "Name": "Clouwer123",
   })
}

删除操作

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func main() {
   DB.AutoMigrate(&Student{})
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //delete删除
   var student Student
   //单个删除
   DB.Delete(&student, 10)
   //批量删除
   DB.Delete(&student, []int{7, 8, 9})
   //常用的从表头顺序删除
   DB.Take(&student)
   DB.Delete(&student)
}

钩子函数

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func (user *Student) BeforeCreate(tx *gorm.DB) (err error) {
   Email := "test@qq.com"
   user.Email = &Email
   return nil
}

func main() {
   DB.AutoMigrate(&Student{})
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //创建hook(钩子函数)
    //用hook在插入数据表前做一些事情(这里的举例是在表创建之前给一些数据赋值进行创建)
    Emailtest := "testest@qq.com"
    DB.Create(&Student{
        Name:  "Clouwer100",
        Age:   23,
        Email: &Emailtest,    //没用,该值早就被赋值了
    })
}

高级查询

Where:等价于SQL语句中的where查询

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //先创建表,创完就可以注释掉了
   //var studentList []Student
   //DB.Find(&studentList).Delete(&studentList)
   //studentList = []Student{ //注意,go语言的切片,最先被初始化的最后被输出,所以要顺序执行得逆向写。
   // {ID: 9, Name: "克劳德", Age: 32, Email: PtrString("lyf@qq.com"), Gender: true},
   // {ID: 8, Name: "扎克斯", Age: 18, Email: PtrString("zhangwu@qq.cn"), Gender: true},
   // {ID: 7, Name: "爱丽丝", Age: 23, Email: PtrString("ff@163.com"), Gender: false},
   // {ID: 6, Name: "蒂法", Age: 54, Email: PtrString("liuda@qq.com"), Gender: false},
   // {ID: 5, Name: "萨菲罗斯", Age: 23, Email: PtrString("liwu@lly.cn"), Gender: true},
   // {ID: 4, Name: "李衣李", Age: 14, Email: PtrString("liqi@qq.com"), Gender: false},
   // {ID: 3, Name: "克里克", Age: 25, Email: PtrString("xiaomeo@qq.com"), Gender: true},
   // {ID: 2, Name: "阿童木", Age: 26, Email: PtrString("ruyan@yf.com"), Gender: true},
   // {ID: 1, Name: "盖克特", Age: 21, Email: PtrString("moling@sl.com"), Gender: true},
   //}
   //DB.Create(&studentList)

   var students []Student
   // 查询用户名是盖克特的
   // select * from students where name = ‘盖克特‘
   DB.Where("name = ?", "盖克特").Find(&students)
   fmt.Println(students)

   // 查询用户名不是盖克特的(三种写法,三条都等价)
   // select * from students where not name = ‘盖克特’
   //fmt.Println(DB.Not("name = ?", "盖克特").Find(&students).RowsAffected)
   //DB.Where("not name = ?", "盖克特").Find(&students)
   DB.Where("name <> ?", "盖克特").Find(&students)
   fmt.Println(students)

   // 查询用户名包含 李衣李,克里克(这里的?不用加`()`,但是Find方法这样写就要加`()`)
   // select * from students where name in (‘李衣李’,‘克里克’)
   //DB.Find(&students, "name = (?)", []string{"李衣李", "克里克"})
   DB.Where("name in ?", []string{"李衣李", "克里克"}).Find(&students)
   fmt.Println(students)

   // 查询姓李的(两种方法:%号不限字数,_有多少个就多查多少个)
   // select * from students where name like ‘李%’
   //DB.Where("name like ?", "李__").Find(&students)
   DB.Where("name like ?", "李%").Find(&students)
   fmt.Println(students)

   // 查询年龄大于23,是qq邮箱的
   // select * from student where age > 23 and email like '%@qq.com'
   DB.Where("age > ? and email like ?", "23", "%@qq.com").Find(&students)
   fmt.Println(students)

   // 查询是qq邮箱的,或者是女的
   // select * from student where age > 23 or gender = 'false'
   DB.Where("gender = ? or email like ?", false, "%@qq.com").Find(&students)
   //DB.Where("gender = ?").Or("email like ?", false, "%@qq.com").Find(&students)
   fmt.Println(students)
}

使用结构体查询

使用结构体查询,会过滤零值

并且结构体中的条件都是and关系

//使用结构体查询
//会过滤零值
DB.Where(&Student{Name: "蒂法", Age: 54}).Find(&students)
fmt.Println(students)
DB.Where(&Student{Name: "克劳德", Age: 0}).Find(&students)
fmt.Println(students)

//[0.000ms] [rows:1] SELECT * FROM `Students` WHERE `Students`.`Name` = '蒂法' AND`Students`.`Age` = 54
//[{6 蒂法 54 false 0xc00021b2f0}]

//[0.000ms] [rows:1] SELECT * FROM `Students` WHERE `Students`.`Name` = '克劳德'  
//[{9 克劳德 32 true 0xc00021b400}]

使用Map查询

不会过滤零值

//使用Map查询
//不会过滤零值
DB.Where(map[string]any{"Name": "克劳德", "Age": 32}).Find(&students)
fmt.Println(students)
DB.Where(map[string]any{"Name": "克劳德", "Age": 0}).Find(&students)
fmt.Println(students)

//[0.000ms] [rows:1] SELECT * FROM `Students` WHERE `Age` = 32 AND `Name` = '克劳德'
//[{9 克劳德 32 true 0xc00021b520}]

//[0.000ms] [rows:0] SELECT * FROM `Students` WHERE `Age` = 0 AND `Name` = '克劳德'
//[]

使用select部分属性查询

//使用部分字段查询(Find查询是直接查全部)
//select一个属性,这样只有那个属性是真实值,其他没有select的全是零值/空值
DB.Select("Name").Find(&students)
fmt.Println(students)
//传多个属性
DB.Select("Name", "Age").Find(&students)
fmt.Println(students)
//传一个切片
DB.Select([]string{"Name", "Age"}).Find(&students)
fmt.Println(students)

//写一个小的结构体,去做到部分属性查询
type PartInfo1 struct {
    Name string
    Age  int
}
var info1 []PartInfo1
//这样写会select两次,这是这种方法的弊端
DB.Select("Name", "Age").Find(&students).Scan(&info1) //要有Find的原因是因为要知道表名
fmt.Println(students)
//当知道表名时可以这样↓,和上面操作等价
DB.Table("students").Select("Name", "Age").Scan(&info1)
fmt.Println(students)

//下面这个就是改进方法,只会select一次
type PartInfo2 struct {
    Name string
    Age  int
}
var info2 []PartInfo2
DB.Model(&Student{}).Select("Name", "Age").Scan(&info2)
fmt.Println(students)

注意:

//Scan是根据列名扫描的
//例子
type PartInfo2 struct {
   //Name1 string //按道理来说应该查不到,但是这里能,我也不清楚
   Name1 string `gorm:"column:Name"` //按道理来说应该这样写才查询的到
   Age   int
}
var info2 []PartInfo2
DB.Model(&Student{}).Select("Name", "Age").Scan(&info2)
fmt.Println(students)

排序

desc:降序

asc:升序

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   var studentList []Student
   //降序
   //DB.Order("Age desc").Find(&studentList)
   //升序
   DB.Order("Age asc").Find(&studentList)
   fmt.Println(&studentList)
}

分页查询

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   var studentList []Student
    //分页查询
    // select id, name, age students limit 2    和下面等价
    // select id, name, age students limit 2 offset 0    第一页    2指一页只能有2行    0指从第0行开始
    // select id, name, age students limit 2 offset 2    第二页    2指一页只能有2行    2指从第2行开始
    // select id, name, age students limit 2 offset 4    第三页    ......
    // select id, name, age students limit 2 offset 6    第四页    ......
    DB.Limit(2).Offset(0).Find(&studentList) //和上面SQL语句一样
    fmt.Println(&studentList)
    DB.Limit(2).Offset(2).Find(&studentList) //和上面SQL语句一样
    fmt.Println(&studentList)
    DB.Limit(2).Offset(4).Find(&studentList) //和上面SQL语句一样
    fmt.Println(&studentList)
    //......
}

通用写法:

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //先创建表,创完就可以注释掉了
   var studentList []Student
   //分页查询的通用写法
   limit := 2
   page := 1
   //offset那是一个偏移的计算,其实就是每页多少行,然后根据行数来进行分页计算
   DB.Limit(limit).Offset((page - 1) * limit).Find(&studentList)
   fmt.Println(&studentList)
}

去重

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })
   
   //去重
   //select distinct age from students
   var ageList []int
   //写法一
   //DB.Model(Student{}).Select("Age").Distinct("Age").Scan(&ageList)
   //[0.720ms] [rows:8] SELECT DISTINCT `Age` FROM `Students`
   //&[21 26 25 14 23 54 18 32]
   //写法二
   DB.Model(Student{}).Select("distinct Age").Scan(&ageList)
   //[1.511ms] [rows:8] SELECT distinct Age FROM `Students`
   //&[21 26 25 14 23 54 18 32]
   fmt.Println(&ageList)
}

分组查询

package main

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
   "gorm.io/gorm/logger"
   "gorm.io/gorm/schema"
)

var DB *gorm.DB
var mysqlLogger logger.Interface

func init() {
   username := "root1"    //账号
   password := "rootroot" //密码
   host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
   port := 3306           //数据库端口
   DBname := "GoORM"      //数据库名
   timeout := "10s"       //连接超时,10秒

   mysqlLogger = logger.Default.LogMode(logger.Info)

   // root:root@tcp(127.0.0.1:3306)/gorm?
   dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
   //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
      //跳过默认事故,它默认是false,在初始化时禁用它,这样可以获得60%的性能提升
      //SkipDefaultTransaction: true,

      //如果不想自动以蛇形复数那些默认形式生成表,就写在这里面
      NamingStrategy: schema.NamingStrategy{
         SingularTable: false, // 是否单数表名
         NoLowerCase:   true,  // 是否关闭小写转换
      },
   })
   if err != nil {
      panic("连接数据库失败, error=" + err.Error())
   }
   DB = db
   // 连接成功
}

type Student struct {
   ID     uint   `gorm:"size:3" json:"id"` //这里可以加json定义名字
   Name   string `gorm:"size:12" json:"name"`
   Age    int    `gorm:"size:3"`
   Gender bool
   Email  *string `gorm:"size:32"`
}

func PtrString(email string) *string {
   return &email
}

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })
   
    //分组查询
    var GenderList []int
    //查询男女生个数
    DB.Table("students").Select("count(id)").Group("Gender").Scan(&GenderList)
    fmt.Println(&GenderList)
    //&[6 3]
    //但这样我们不知道哪个是男哪个是女的个数
}

详细一点:

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })
    //分组查询
    var GenderList []int
    //查询男女生个数
    DB.Table("students").Select("count(id)").Group("Gender").Scan(&GenderList)
    fmt.Println(&GenderList)
    //&[6 3]
    //但这样我们不知道哪个是男哪个是女的个数

    //这样相对来说更精确一点
    type Group struct {
        Count  int
        Gender string
    }
    var GroupList []Group
    DB.Model(Student{}).Select("Gender", "count(id) as Count").Group("Gender").Scan(&GroupList)
    fmt.Println(GroupList)
}

更详细一点:

func main() {
   //日志显示
   DB = DB.Session(&gorm.Session{
      Logger: mysqlLogger,
   })

   //更精确一点
   type Group struct {
      Count    int
      Gender   string
      NameList string
   }
   var GroupList []Group
   DB.Model(Student{}).
      Select(
         "group_concat(Name) as NameList",
         "Gender",
         "count(id) as Count",
      ).
      Group("Gender").
      Scan(&GroupList)
   fmt.Println(GroupList)
    //[0.506ms] [rows:2] SELECT group_concat(Name) as NameList,`Gender`,count(id) as Count FROM `Students` GROUP BY `Gender`
    //[{3 0 李衣李,蒂法,爱丽丝} {6 1 盖克特,阿童木,克里克,萨菲罗斯,扎克斯,克劳德}]

}
Gorm框架的好处:贴近原生SQL的编写

执行原生SQL语句

//执行原生SQL语句
type Group struct {
   Count    int
   Gender   string
   NameList string
}
var GroupList []Group
DB.Raw("SELECT group_concat(name) as NameList, `Gender`,count(id) as Count FROM `Students` GROUP BY `Gender`").Scan(&GroupList)
fmt.Println(GroupList)
//哪里要拼接就用?进行传递

子查询

获取上一个查询作为参数进行下一个查询

//子查询
var studentList []Student
//原生sql
//select * from students where age > (select avg(age) from students);
DB.Model(Student{}).Where("age > (?)", DB.Model(Student{}).Select("avg(Age)")).Find(&studentList)
fmt.Println(studentList)

命名参数

//命名参数
var users []Student
//@具名参数,?不具名参数
DB.Where("Name = @name and Age = @age", sql.Named("name", "克劳德"), sql.Named("age", 32)).Find(&users)
fmt.Println(users)
//map的形式,更简短也更简洁,而且可以进一步的封装
DB.Where("Name = @name and Age = @age", map[string]any{"name": "盖克特", "age": 21}).Find(&users)
fmt.Println(users)

find到map

//find到map
var res []map[string]any    //转成的类型随意,json那些都可以
DB.Table("students").Find(&res)
fmt.Println(res)

查询引用Scope

func main() {    
    ......
    var Map []map[string]any
   //DB.Table("students").Find(&res)
   //查询引用Scope
   DB.Table("students").Scopes(FindAge23).Find(&Map)
   fmt.Println(Map)
}

// 这里相当于封装语句,然后再Scopes里链式调用
func FindAge23(db *gorm.DB) *gorm.DB {
   return db.Where("age > ?", 23)
}
PREV
[机器学习]小摸一下GAN网络
NEXT
[Golang]Gorm框架 关系操作

评论(0)

发布评论