gorm框架(一)|青训营笔记

117 阅读5分钟

1.连接

需要下载MySQL的驱动

go get gorm.io/driver/mysql
go get gorm.io/gorm

简单连接

package gorm
​
import (
    "fmt""gorm.io/driver/mysql"
    "gorm.io/gorm"
)
var DB *gorm.DB
func Init() (*gorm.DB){
    //1.
    username := "root"    //账号
    password := "qwe123." //密码
    host := "127.0.0.1"   //数据库地址,可以是IP或者地址名
    port := 3306          //端口号
    Dbname := "xiangmu"   //数据库名
    timeout := "10s"      //连接超时,10秒
    //2.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)//转换为字符串
    //3.连接MySQL,获得DB类型实例,用于后面的数据库读写操作
    db, err := gorm.Open(mysql.Open(dsn)) 
    if err != nil {
        panic("连接数据库失败,error=" + err.Error())
    }
    //4.连接成功
    DB=db
    return DB
}
func main(){
    包名.Init.PrintF
}

高级配置

跳过默认事务

为了确保数据一致性,GORM会在事务里执行写入操作(创建,更新,删除)。如果没有这方面的要求,可以在初始化时禁用它

这样还可以获得60%的性能提升

db,err:=gorm.Open(mysql.Open("gorm.db"),&gorm.Config{
    SkipDefaultTransaction:true,
})

命名策略

gorm采用命名策略是,表名是蛇形复数,字段名是蛇形单数

例如:

var DB *gorm.DB
type Student struct{
    ID     uint
    MyName string
    Age    int
}
func main(){
    DB.AutoMigrate(&Student{})//创建表
}

gorm会为我们这样生成表结构

CREATE TABLE `students`(`id` bigint unsigned AUTO_INCREMENT ,`age` bigint,`my_sname` longtext,PRIMARY KEY(`id`))

我们也可以修改这些策略

db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{
    NameingStrategy: schema.NamingStrategy{
        TablePrefix: "f_",//表名前缀是什么
        SingularTable: false,//是否单数表名
        NoLowerCase: false,//是否关闭小写转换
    }
})

2.显示日志

gorm的默认日志是只打印错误和慢SQL

我们可以自己设置

var mysqlLogger logger.Interface
//要显示的日志等级
mysqlLogger = logger.Default.LogMode(logger.Info)//Info是全局 占用资源多
db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{
    Logger:mysqlLogger
})

如果你想要自定义日志的显示

那么可以使用如下代码

newLogger:=logger.New(
    log.New(os.Stdout,"\r\n",log.LstdFlags),//(日志输出的目标,前缀和日志包含的内容)
    logger.Config{
        SLowThreshold:             time.Second,//慢 SQL 阈值
        LogLevel:                  logger.Info,//日志级别
        IgnoreRecordNotFoundError: true, //忽略ErrRecordNotFound(记录未找到)错误
        Colorful:                  true,//使用彩色打印
    },
)
​
db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{
     Logger:newLogger,
})

部分展示日志(用的多)

var model Student //要开启mysqlLogger = logger.Default.LogMode(logger.Info)
session:=DB.Session(&gorm.Session{Logger:newLogger})
session.First(&model)

实例

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

如果只想某些语句显示日志

DB.Debug.First(&model)
//代码实例
func main() {
    DB.Debug().AutoMigrate(&Student{}) //创建表
}

3.模型定义

模型是标准的struct,由go的基本数据类型,实现了ScannerValuer接口的自定义类型及其指针或别名组成

定义一张表

type Student struct{
    ID uint//默认使用ID作为主键
    Name string
    Email *string//使用指针是为了存空值
}

常识:小写属性是不会生成字段的

自动生成表结构

//可以放多个
DB.AUtoMigrate(&Student{})

AutoMigrate的逻辑是新增,不删除,不修改(大小会修改)

例如将Name修改为Name1,进行迁移,会多出一个name1的字段

生成的表结构如下

CREATE TABLE `students`(`name` longtext,`id` bigint unsigned AUTO_INCREMENT,`name1` longtext,`email` longtext,PRIMARY KEY(`id`))

注意:默认的类型太大了

修改大小

我们可以使用gorm的标签进行修改

有两种方式

Name string `gorm:"type:vachar(12)"`
Name string `gorm:"size:2"`

字段标签

type 定义字段类型

size 定义字段大小

column 自定义列名

primaryKey 将列定义为主键

unique将列定义为唯一键

default 定义列的默认值

not null 不可为空

embedded 嵌套字段

embeddedPrefix 嵌套字段前缀

comment 注释

各个标签之前用;连接

type Student struct {
    ID     uint    `gorm:size:10`
    MyName string  `gorm:size:16`
    Age    int     `form:size:3`
    Email  *string `gorm:size:128`
    Type   string  `gorm:column:type;size:4`
    Date   string  `gorm:"default:2023-4-13;comment:日期"`
}

单表查询

先使用gorm对单张表进行增删改查

表结构

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"`
}

添加记录

email:="xx@qq.com"
//创建记录
Student:=Student{
    Name: "小明",
    Age:21,
    Gender:true,
    Email:&email,
}
DB.Create(&student)

有两个地方需要注意

  1. 指针类型是为了更好的存null类型,但是传值的时候,也记得传指针
  2. Create接收的是一个指针,而不是值

由于我们传递是一个指针,调用完Create后,student这个对象上面就有该记录的信息了,如创建的id

DB.Create(&student)
fmt.Printf("%#v\n",student)
//main.Studnet{ main.Person{ID:0x1, Name:"小明", Age:21, Gender:true, Email:(*string)(0xc0001da6c0)}}

批量插入

Create方法还可以用于插入多条记录

var Plist []Person
for i:=0;i<100;i++{
    Plist = append(Plist,Person{
       Name: fmt.Sprintf("学生:%d号",i+1),
       Age:21+i+1,
       Gender:true,
       Email:&email,
    })
}
DB.Create(&Plist)

查询单条记录

var P1 Person
DB.Take(&P1)
fmt.Println(P1)

获取单条记录的方法很多,我们对比sql就很直观了

DB=DB.Session(&gorm.Session{Logger:Log})
var P1 Person
DB.Take(&P1)//默认第一条
//SELECT *FROM `persons`LIMIT 1
P1=Person{}
DB.First(&P1)//默认第一条
//SELECT *FROM `persons` ORDER BY `persons`.`id` LIMIT 1
P1=Person{}
DB.Last(&P1)//默认最后一条
//SELECT *FROM `persons` ORDER BY `persons`.`id` DESCLIMIT 1

根据其他条件查询

var P1 Person
DB.Take(&P1,"name = ?","小明")
fmt.Println(P1)

使用?作为占位符,将查询的内容放入?

//SELECT *FROM `persons` WHERE name = "小明"LIMIT 1

这样可以有效的防止sql注入

他的原理就是将参数全部转义,如

DB.Take(&P1,"name = ?","小明'or 1=1;#1")
//SELECT * FROM `people` WHERE name='小明'or 1=1;#'  LIMIT 1

根据struct查询

var P1 Person
//只能有一个主要值
Person.ID=2//只能根据主键去查
DB.Take(&P1)
fmt.Println(P1)

获取查询结果

获取查询的记录数

count:=DB.Find(Plist).RowsAffected

是否查询失败

err:=DB.Find(&Plist).Error

查询失败有查询为空,查询条件错误,sql语法错误

可以使用判断

var P1 Person
err:=DB.Take(&P1,"xx").Error
switch err{
    case gorm.ErrRecordNotFound:
    fmt.Println("没有找到")
    default:
    fmt.Println("sql错误")
}

查询多条记录

var PList []Person
    DB.Find(&PList)
    for_,p:=range PList{
    fmt.Println(p)
}
//由于email是指针类型,所以看不到实际的内容
//但是序列号之后,会转换为我们可以看得懂的方式
var PList []Person
DB.Find(&PList)
for_,p:=range PList{
    data,_:=json,Marshal(p)
    fmt.Println(string(data))
}

根据主键列表查询

var PList []Person
DB.Find(&PList,[]int{1,3,5,7})
DB.Find(&PList,1,3,5,7)//一样的
fmt.Println(PList)

根据其他条件查询

DB.Find(&PList,"name in ?",[]string{"小明","小刘"})