GORM的安装和简单使用(增删改查)| 青训营

300 阅读10分钟

安装

我使用的是和 mysql 的连接。

项目创建参考GIN的安装

接下来在 goland 中打开终端,使用 go get 命令来安装库依赖:

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

安装完成后就可以正常使用了。

使用

简单连接

在使用之前我们需要先初始化来建立连接。

这就需要一些数据库的数据:

  • 用户名:username
  • 密码:password
  • 数据库地址:host (一般本地的数据库地址为 127.0.0.1)
  • 端口号:port(一般为 3306)
  • 数据库名:Dbname
  • 连接超时:timeout

接下来是数据库的连接:

username := "root" //用户名  
password := "*********" //密码  
host := "127.0.0.1" //数据库地址,可以是IP或者域名  
port := 3306 //端口号  
Dbname := "gorm" //数据库名  
timeout := "10s" //超时连接,10秒

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{})  
if err != nil {  
	panic("数据库连接失败,error=" + err.Error())  
}

以我的代码为例,我创建了一个名叫 gorm 的数据库,如果连接失败就会抛出异常。

上述操作会写在一个叫 init 的函数里面。在函数之外一般会写一个 *gorm.DB 类型的变量,将连接 mysql 的 db 的值传递过去,便于在其他地方操作数据库。

gorm.Config{}

这个就是 GORM 在数据库建立连接后框架本身做的一些默认配置。

大概有这么多:

type Config struct { 
	SkipDefaultTransaction bool 
	NamingStrategy schema.Namer 
	Logger logger.Interface 
	NowFunc func() time.Time 
	DryRun bool 
	PrepareStmt bool 
	DisableNestedTransaction bool 
	AllowGlobalUpdate bool 
	DisableAutomaticPing bool 
	DisableForeignKeyConstraintWhenMigrating bool 
}

具体参考这篇文章:Go ORM框架 - GORM 踩坑指南

约定

GORM 更倾向于约定而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名。

我们可以修改上述的默认配置:

NamingStrategy: schema.NamingStrategy{  
TablePrefix: "", //表名前缀  
SingularTable: true, //是否使用单数表名,这里表示要单表名  
NoLowerCase: true, //是否大小写转换,这里表示不要小写  
},

将其放在 gorm.Config{} 即可生效。

日志

默认情况下,控制台是没有任何日志输出的。

第一种方式,我们可以在 gorm.Config{} 中进行配置。可以使用 GORM 的 logger.Default.LogMode(logger.Info) 来打印日志。

Logger: logger.Default.LogMode(logger.Info),

将其放在 gorm.Config{} 即可生效。 image.png

第二种方式是在主函数中,对数据库的对象进行操作:

DB = DB.Session(&gorm.Session{  
	Logger: logger.Default.LogMode(logger.Info),  
})

这样也能够显示日志。

第三种方式是在进行一些操作的时候加上一个 Debug()

DB.Debug().AutoMigrate(&Student{}) //新建一个空白表

加上 Debug() 的时候就能够显示日志。

模型

定义一张表:

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

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

自动生成表结构

可以使用函数 AutoMigrate() 函数自动生成表结构,但是它只能新增,不删除也不修改表结构(大小会修改)。

DB.AutoMigrate(&Student{})

例如将结构体 Student 添加一个成员,进行迁移,表中也会新增一个字段。

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

修改字段大小

在默认情况况下,字段的值都非常的大,有些字段可能用不到这么大的存储量,所以就需要进行一些修改。

可以使用字段标签 gorm:"size:x" (x 表示字节个数)来修改存储的大小:

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

这样的话,就可以将字段大小进行细致的修改,size 后面的参数为字节的长度,比如 Name 字段给的是 128 位字节数进行存储。

另外还有一种方式可以修改字段大小:

Name  string  `gorm:"type:varchar(16)"` 

这样也和上面修改的大小一样。

字段标签

type 定义字段类型,也可以顺便定义大小

size 定义字段大小

column 自定义列名

primaryKey 将列定义为主键

unique 将列定义为唯一键

default 定义列的默认值

not bull 不可为空

embedded 嵌套字段

embeddedPrefix 嵌套字段前缀

comment 注释

多个标签使用 ; 隔开。

column 字段需要注意,后面的字符串是什么,字段名就是什么,要注意有没有多添加一个空格或者其他符号

表单的增删改查

插入单个数据

首先需要新建一个模型 struct:

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

我们就可以在 main 函数中利用之前学到的连接数据库来创建表:

DB.AutoMigrate(&Student{})

我们如何向表中添加数据呢?

我们的表是根据结构体来创建的,所以添加数据的过程就相当于是实例化结构体。

首先先实例化一个数据:

email := "123456789@qq.com"  
//添加记录,就相当于实例化结构体  
s1 := Student{  
	Name: "cfd",  
	Age: 20,  
	Gender: true,  
	Email: &email,  //注意 Email 的值是指针
}

err := DB.Create(&s1).Error  
fmt.Println(err) //在这里直接输出

image.png

此时就将数据创建成功了。

如果我们不写 Name 的话,name 就会是空值,其他列也是一样。因为除了主键不为空,其他的都没有设置不能为空。

  1. 设置 email 为指针类型是因为这样可以更好的传递空值,直接赋值为 nil 就行

  2. 另外,Create 函数传递的是指针,所以当添加完一个数据之后,s1 的所有值也都被赋值了,比如 ID 就被赋值了

  3. 在 Create 函数后面加上 .Error 就可以获取它的报错信息,如果为空,则没有报错,反之,就能够更好地查看报错原因。

插入多个数据

知道了如何插入单个数据后,批量插入的方式就变得很简单。

我们先定义一个切片,存放多个学生数据:

var studentList []Student  
for i := 0; i < 10; i++ {  
	s1 := Student{  
	Name: fmt.Sprintf("cfd%d号", i+1), //Sprintf 的返回值为字符串 
	Age: 20 + i + 1,  
	Gender: true,  
	Email: nil,  
	}  
	studentList = append(studentList, s1)  
}

然后使用 Create 函数即可:

DB.Create(&studentList) //将整个切片传递进去

image.png

查找单条数据数据可以使用 Take 函数:

var student Student  
DB.Take(&student)
fmt.Println(student)
student = Student{}

在默认情况下查找的是表中的第一条数据。函数会将查询来的值放到 student 中。

还有 First 函数和 Last 函数,分别查询表中的第一条数据和最后一条数据:

DB.First(&student) //查找第一条数据
fmt.Println(student)
student = Student{}

DB.Last(&student) //查找最后一条数据
fmt.Println(student)
student = Student{}

除此之外,还可以根据条件来查询。

  • 根据主键来查询:
DB.Take(&student, 4) //查询主键为 4 的数据  

第二个参数为主键,也可以写成字符串形式:"4",效果是一样的。

  • 根据其他条件查询:
DB.Take(&student, "name = ?", "cfd3号") //查询名字为 cfd3号 的数据

使用 ? 作为占位符,来将查询的内容放到 ? 中,可以有效地防止 sql 注入。

根据 struct 查询

我们还可以使用 struct 来查询。

例如给 Student 的一个实例 student 一个主要值:

student.ID = 5
DB.Take(&student)  
fmt.Println(student)  
student = Student{}

上述例子是查询 ID 为 5 的数据。但是需要注意的是,只能有一个主要值

但是不能使用除了主键之外的值比如名字 name 来查询或者年龄 age 来查询

获取查询结果

获取查询的记录数。

我们可以通过 Find 函数的 RowsAffected 来获取查询的记录数:

student.ID = 5  
count := DB.Find(&student).RowsAffected  
fmt.Println(count)  //结果为 1

是否查询失败。

我们可以通过 Find 函数的 Error 来获取查询是否失败:

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

查询失败的原因有很多种,比如查询条件错误、sql 的语法错误等等。因此可以使用 gorm.ErrRecordNotFound 来进行判断:

err := DB.Take(&student, 3).Error  
if err!=nil {  
	switch err {  
	case gorm.ErrRecordNotFound:  
		fmt.Println("没有找到")  
	default:  
		fmt.Println("sql语句错误")  
	}  
}
查询所有数据

可以使用 Find 函数查询所有的数据:

var studentList []Student  
count := DB.Find(&studentList).RowsAffected
fmt.Printf("查询条数为%d\n", count)  
for _, std := range studentList {  
	fmt.Println(std)  
}

上述代码是查询表中所有的数据全部储存到 studentList 这个切片中,然后逐一打印出来,还获取到了查询次数。

我们还可以将查询到的数据转化为 json 数据:

//将查询到的数据转化为json数据  
data, _ := json.Marshal(studentList)  
fmt.Printf(string(data))  

由于储存的 email 是指针类型,所以直接打印的时候会打印地址。 image.png

而将其转化为 json 数据后,序列化之后就能够让我们看懂数据是什么。 image.png

根据 ID 列表查询

我们依次输入多个 ID 来进行查询,我们将它称为 ID 列表:

var studentList []Student  
DB.Find(&studentList, []int{3, 4, 5, 6, 7, 8, 9})  
fmt.Println(studentList)

这样我们就查询了 ID 为 3, 4, 5, 6, 7, 8, 9 的数据。当然切片类型不止为 int 类型还可以是 string 类型。

使用其他条件列表查询

除了使用 ID 列表来进行查询之外,还可以使用其他条件列表来进行查询。

例如使用名字列表来查询:

var studentList []Student  
DB.Find(&studentList, "name in (?)", []string{"cfd4号", "cfd5号", "cfd6号"})  
fmt.Println(studentList)

需要注意的是,中间的占位符应该加上括号,并且使用 in

Save

用于单个记录的全字段更新。它会保存所有字段,哪怕是零值。

比如需要更改 ID 为 3 的这一条数据,我们首先需要找出这条数据:

var student Student  
DB.Take(&student, 3)

现在把数据存储到 student 里面了,我们将年龄修改成 60:

student.Age = 60

接着就可以使用 Save 函数进行更新:

DB.Save(&student)

我们看这条日志显示的 SQL 语句: image.png 根据这个 SQL 的语句,我们发现它会把所有字段全部更新一遍。所以说,我们就可以一次性更新这条数据的许多个值

当然,也可以选择性的只更新某一个字段,这就需要用到 Select 函数。

比如我只想更新 age 字段:

DB.Debug().Select("age").Save(&student) //实现了单个字段的更新

image.png

update

使用 update 函数可以让很多条数据的某一字段更新。

比如我们让 ID 为 3,4,5,6,7 的数据的 gender 字段全部改成 false:

var studentList []Student  
DB.Find(&studentList, []int{3, 4, 5, 6, 7}).Update("gender", false)

需要注意的是,Update 函数必须在 Find 函数后面点出,否则没有用。

这是输出的日志里面的 SQL 语句:

image.png

updates

这个相比于上面一个,是可以来更新多个字段的数据。

首先看看使用结构体的方式更新数据

比如我们可以让 ID 为 3,4,5,6,7 的数据的 age 字段的数据全部改成 50 并且将 gender 字段全部改成 true:

var studentList []Student  
DB.Find(&studentList, []int{3, 4, 5, 6, 7}).Debug().Updates(Student{  
	Age: 60,  
	Gender: true,  
})

updates 是传入一个结构体,写入需要更改的数据。但是他不能将数据修改为零值。比如在这个例子中,我想把 gender 字段全部改成 false,也就是零值。运行之后 gender 的值并不会改变。

另一种方式是使用 map 这个数据结构来更新数据,就可以将数据修改为零值:

var studentList []Student  
DB.Find(&studentList, []int{3, 4}).Debug().Updates(map[string]any{  
	"gender": false,  
})

这里的 "gender" 是数据库里表的字段名。这样就可以 gender 字段全部改成 false。

删除操作就只有一个 Delete 函数。

可以根据主键进行删除:

var student Student  
DB.Delete(&student, 12)

这样就删除了 ID 为 12 的数据。

Delete 函数一般是配合查询函数一起使用,查询到某一条数据后将其删除:

var student Student  
DB.Take(&student, 11)  
DB.Delete(&student)

这样就删除了 ID 为 11 的数据。

Delete 函数还能批量删除,用法和前面的 Find 函数类似:

var student Student  
DB.Delete(&student,[]int{3,4,5,6,7})

这样就删除了 ID 为 3,4,5,6,7 的数据。

完整代码地址:hdheid/GormStudy