Skip to content
gorm学习笔记

中文文档v1:jinzhu版: https://v1.gorm.io/zh_CN/docs/

中文文档v2:gorm.io版: https://gorm.io/zh_CN/

当前博客内容参考v1版本做的,v2版本与当前笔记可能会有所差别

安装GORM

go
go get -u github.com/jinzhu/gorm

连接数据库

go
package main

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql" //引入mysql的驱动
)

func main() {
	//连接数据库:
	//Open传入两个参数:
	//第一个参数:指定你要连接的数据库
	//第二个参数:指的是数据库的设置信息:用户名:密码@tcp(ip:port)/数据库名字?charset=utf8&parseTime=True&loc=Local
	//charset=utf8设置字符集
	//parseTime=True为了处理time.Time
	//loc=Local 时区设置,与本地时区保持一致
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
}

运行

如果运行中报错如下

missing go.sum entry for module providing package github.com/go-sql-driver/mysql (imported by github.com/jinzhu/gorm/dialects/mysql); to add:

执行如下命令即可解决

go
go get -u github.com/go-sql-driver/mysql

创建和删除表以及判断表是否存在

go
package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

// 定义结构体:
type User struct {
	Age  int
	Name string
}

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&User{})
	//Table方法可以指定你要创建的数据库的表名
	db.Table("user").CreateTable(&User{})
	//删除表:
	//db.DropTable(&User{}) //通过&User{}来删除users表
	//db.DropTable("user") //通过"user"删除user表
	//判断表是否存在:
	flag1 := db.HasTable(&User{}) //判断是否有users表
	fmt.Println(flag1)
	flag2 := db.HasTable("user") //判断是否有user表
	fmt.Println(flag2)
}

简单的增删改查

go
package main
import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)
//定义结构体:
type User struct {
	Age int
	Name string
}
func main(){
	//连接数据库:
	db,err := gorm.Open("mysql","root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&User{})

	//增删改查:
	//增加数据:
	// db.Create(&User{Age:18,Name:"丽丽"})
	//查询数据:第一个参数:查询出来的数据的载体:
	var myuser User
	db.First(&myuser,"age = ?",18)
	fmt.Println(myuser)
	//更新数据:
	//需要做的:先查询,再更新
	db.Model(&myuser).Update("age",30)
	db.Model(&myuser).Update("name","菲菲")
	//删除数据:
	//需要做的:先查询,再删除
	db.Delete(&myuser)
}

模型名与表名的映射

【1】模型名和表名的映射规则:

(1)如果模型名没有驼峰命名,那么表名就是:模型名小写+复数形式: 如模型名User-》表名users (2)如果模型名有驼峰命名,那么表名就是:大写变小写并在前面加下划线,最后加复数形式:如模型名UserInfo-》表名user_infos (3)如有模型名有连续的大写字母,那么表名就是:连续的大写字母变小写,驼峰前加下划线,字母变小写,最后加复数形式:如模型名:DBUserInfo-》表名db_user_infos

PS:模型中字段名称与表中列名的映射规则同上,学习视频中未曾演示,可自行练习使用

go
package main

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

// 定义结构体:
type User struct {
	Age  int
	Name string
}
type UserInfo struct {
	Age  int
	Name string
}
type DBUserInfo struct {
	Age  int
	Name string
}
type MyUser struct {
	Age  int
	Name string
}

func (MyUser) TableName() string {
	return "test_my_user"
}
func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&User{})
	db.CreateTable(&UserInfo{})
	db.CreateTable(&DBUserInfo{})
	db.CreateTable(&MyUser{})

}

gorm.Model_匿名字段

【1】gorm.Model匿名字段 只需要在自己的模型中指定gorm.Model匿名字段,即可在数据库表中包含四个字段:ID,CreatedAt,UpdatedAt,DeletedAt

ID:主键自增长

CreatedAt:用于存储记录的创建时间

UpdatedAt:用于存储记录的修改时间

DeletedAt:用于存储记录的删除时间

【2】代码:

go
package main

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type MyUser2 struct {
	//增加一个匿名字段:
	gorm.Model
	Age  int
	Name string
}

func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:

	db.CreateTable(&MyUser2{})

}

结构体标签gorm

【1】通过结构体标签gorm来实现表的约束 【2】gorm标签属性值: (1)-: 忽略,不映射这个字段 eg: gorm:"-" ,适合:一些冗余字段,不想在数据库中体现,只想在结构体中体现 (2)primary_key:主键 eg: gorm:"primary_key" PS:如果是想要加联合主键,在每个字段后加入 gorm:"primary_key"即可 例如:即可将StuID和Name作为联合主键 (3)AUTO_INCREMENT:自增 eg: gorm:"AUTO_INCREMENT" (4)not null:不为空,默认为空 eg: gorm:"not null" (5)index:索引, eg: gorm:"index" 创建索引并命名:eg: gorm:"index:idx_name_code" (6)unique_index:唯一索引 eg: gorm:"unique_index" 唯一性索引unique index和一般索引normal index最大的差异就是在索引列上增加了一层唯一约束。添加唯一性索引的数据列可以为 空,但是只要存在数据值,就必须是唯一的。 (7)unique:唯一 eg: gorm:"unique" (8)column:指定列名 eg: gorm:"column:user_name" (9)size:字符串长度,默认为255 eg:gorm:"size:10" (10)default default:'男' 默认值 (11)type:设置sql类型 eg: gorm:"type:int(2)" PS:多个属性值之间用分号分隔

结构体代码

go
type Student struct {
        StuID int `gorm:"primary_key;AUTO_INCREMENT"`
        Name string `gorm:"not null"`
        Age int `gorm:"unique_index"`
        Email string `gorm:"unique"`
        Sex string `gorm:"column:gender;size:10"`
        Desc string `gorm:"-"`
        Classno string `gorm:"type:int"`
}

多表操作

表关系

根目录下创建demostruct/demostruct.go文件

一对一

demostruct.go

go
package demostruct

type User struct {
	UserId int `gorm:"primary_key;AUTO_INCREMENT"`
	Age    int
	Name   string
}
type UserInfo struct {
	InfoID  int `gorm:"primary_key;AUTO_INCREMENT"`
	Pic     string
	Address string
	Email   string
	//关联关系
	User User
	//指定外键
	UserId int
}

main.go

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type MyUser2 struct {
	//增加一个匿名字段:
	gorm.Model
	Age  int
	Name string
}

func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&demostruct.User{})
	db.CreateTable(&demostruct.UserInfo{})

}

PS:并没有在数据库中强制加入外键,只是一种关联关系而已

【1】通过gorm标签来指定外键:(属于关系:关系和外键的指定在同一方)

go
package demostruct
type User struct{
        UserId int `gorm:"primary_key;AUTO_INCREMENT"`
        Age int
        Name string
}
type UserInfo struct {
        InfoID int `gorm:"primary_key;AUTO_INCREMENT"`
        Pic string
        Address string
        Email string
        //关联关系
        User User `gorm:"ForeignKey:MyUserID;AssociationForeignKey:UserId"`
        //指定外键:
        MyUserID int
}

【2】通过gorm标签来指定外键:(包含关系:关系和外键的指定不在同一方)

go
package demostruct
type User struct{
        UserId int `gorm:"primary_key;AUTO_INCREMENT"`
        Age int
        Name string
        //指定外键:
        IID int
}
type UserInfo struct {
        InfoID int `gorm:"primary_key;AUTO_INCREMENT"`
        Pic string
        Address string
        Email string
        //关联关系
        User User `gorm:"ForeignKey:IID;AssociationForeignKey:InfoID"`
}

一对多

demostruct.go

go
package demostruct

type Author struct {
	AID  int `gorm:"primary_key;AUTO_INCREMENT"`
	Name string
	Age  int
	Sex  string
	//关联关系:
	Article []Article `gorm:"ForeignKey:AuId;AssociationForeignKey:AID"`
}
type Article struct {
	ArId    int `gorm:"primary_key;AUTO_INCREMENT"`
	Title   string
	Content string
	Desc    string
	//设置外键:
	AuId int
}

main.go

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type MyUser2 struct {
	//增加一个匿名字段:
	gorm.Model
	Age  int
	Name string
}

func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:
	db.CreateTable(&demostruct.Author{})
	db.CreateTable(&demostruct.Article{})

}

多对多

demostruct.go

go
package demostruct

type Student struct {
	SId  int `gorm:"primary_key"`
	SNo  int
	Name string
	Sex  string
	Age  int
	//关联表:
	Course []Course `gorm:"many2many:Student2Course"`
}
type Course struct {
	CId         int `gorm:"primary_key"`
	CName       string
	TeacherName string
	Room        string
}

main.go

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type MyUser2 struct {
	//增加一个匿名字段:
	gorm.Model
	Age  int
	Name string
}

func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:
	db.CreateTable(&demostruct.Student{})
	db.CreateTable(&demostruct.Course{})

}

一对一操作

根目录下创建demostruct/demostruct.go文件

关联添加

demostruct.go

go
package demostruct
type User struct{
        UserId int `gorm:"primary_key;AUTO_INCREMENT"`
        Age int
        Name string
        //指定外键:
        IID int
}
type UserInfo struct {
        InfoID int `gorm:"primary_key;AUTO_INCREMENT"`
        Pic string
        Address string
        Email string
        //关联关系
        User User `gorm:"ForeignKey:IID;AssociationForeignKey:InfoID"`
}

main.go

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&demostruct.User{})
	db.CreateTable(&demostruct.UserInfo{})
	//关联添加数据: (因为关联关系在UserInfo表中,所以添加操作从UserInfo来入手)
	userinfo := demostruct.UserInfo{
		Pic:     "/upload/1.jpg",
		Address: "北京海淀区",
		Email:   "124234@126.com",
		User: demostruct.User{
			Age:  19,
			Name: "丽丽",
		},
	}
	db.Create(&userinfo)
}

关联查询

Association方式(较麻烦,较繁琐)

Association方式查询缺点:先First查询,再Association查询,费劲

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&demostruct.User{})
	db.CreateTable(&demostruct.UserInfo{})
	//关联添加数据: (因为关联关系在UserInfo表中,所以添加操作从UserInfo来入手)
	//userinfo := demostruct.UserInfo{
	//	Pic:     "/upload/1.jpg",
	//	Address: "北京海淀区",
	//	Email:   "124234@126.com",
	//	User: demostruct.User{
	//		Age:  19,
	//		Name: "丽丽",
	//	},
	//}
	//db.Create(&userinfo)

	//关联查询操作:(关联关系在UserInfo表中,所以从UserInfo入手)
	var userinfo demostruct.UserInfo
	//如果只是执行下面这步操作,那么关联的User信息是查询不到的:
	db.First(&userinfo, "info_id = ?", 1)
	// db.Debug().First(&userinfo, "info_id = ?", 1) // 通过Debug可以查看执行的具体sql语句
	fmt.Println(userinfo) //{1 /upload/1.jpg 北京海淀区 124234@126.com {0 0  0}}
	//如果想要查询到User相关内容,必须执行如下操作:
	//Model参数:要查询的表数据,Association参数:关联到的具体的模型:模型名字User(字段名字)
	//Find参数:查询的数据要放在什么字段中&userinfo.User
	db.Model(&userinfo).Association("User").Find(&userinfo.User)
	// db.Debug().Model(&userinfo).Association("User").Find(&userinfo.User) // 通过Debug可以查看执行的具体sql语句
	fmt.Println(userinfo) //{1 /upload/1.jpg 北京海淀区 124234@126.com {1 19 丽丽 1}}

}

Preload方式

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//创建表:通常情况下,数据库中新建的标的名字是结构体名字的复数形式,例如结构体User,表名 users
	db.CreateTable(&demostruct.User{})
	db.CreateTable(&demostruct.UserInfo{})

	//关联查询操作:(关联关系在UserInfo表中,所以从UserInfo入手)
	var userinfo demostruct.UserInfo
	//查询info_id=1的数据放入userinfo中,并关联查询到User字段对应的数据
	db.Preload("User").Find(&userinfo, "info_id = ?", 1)
	fmt.Println(userinfo) //{1 /upload/1.jpg 北京海淀区 124234@126.com {1 19 丽丽 1}}
}

Related方式

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联查询操作:(关联关系在UserInfo表中,所以从UserInfo入手)
	var userinfo demostruct.UserInfo
	db.First(&userinfo, "info_id = ?", 1)
	fmt.Println(userinfo)

	var user demostruct.User
	//通过userinfo模型查出来的User字段的信息放入新的容器user中:
	db.Model(&userinfo).Related(&user, "User")
	fmt.Println(user)
	fmt.Println(userinfo)
}

关联更新

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联更新
	//先查询
	var userinfo demostruct.UserInfo
	db.Preload("User").Find(&userinfo, "info_id = ?", 1)
	fmt.Println(userinfo)
	//再更新:注意:Update的参数age可以用结构体中字段Age也可以用数据库age字段
	db.Model(&userinfo.User).Update("age", 31)
	fmt.Println(userinfo)

}

关联删除

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联删除
	//先查询
	var userinfo demostruct.UserInfo
	db.Preload("User").Find(&userinfo, "info_id = ?", 1)
	fmt.Println(userinfo)
	//再删除:借助userinfo模型删除User记录
	db.Delete(&userinfo.User) //UserInfo中信息没有被删除,删除的是关联的User表中的记录
	db.Delete(&userinfo)
}

一对多操作

使用上面一对多的创建表的代码先创建数据表

关联添加

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type MyUser2 struct {
	//增加一个匿名字段:
	gorm.Model
	Age  int
	Name string
}

func main() {
	//连接数据库:

	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联添加:一对多关系,一个作者对应多个文章,关联关系在作者中,所以我们操作的模型是作者的模型:
	author := demostruct.Author{
		Name: "张三",
		Age:  30,
		Sex:  "男",
		Article: []demostruct.Article{
			{
				Title:   "HTML入门",
				Content: "HTML******",
				Desc:    "好的不得了",
			},
			{
				Title:   "CSS入门",
				Content: "CSS******",
				Desc:    "好的不得了2",
			},
		},
	}
	db.Create(&author)

}

关联查询

Association

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联查询:
	//Association方式查询:因为关联关系在Author中,所以我们操作的是Author模型
	var author demostruct.Author
	//如果只是执行下面这步操作,那么关联的Article信息是查询不到的:
	db.First(&author, "a_id = ?", 1)
	fmt.Println(author) //{1 张三 30 男 []}
	//如果想要查询到Article相关内容,必须执行如下操作:
	//Model参数:要查询的表数据,Association参数:关联到的具体的模型:模型名字Article(字段名字)
	//Find参数:查询的数据要放在什么字段中&author.Article
	db.Model(&author).Association("Article").Find(&author.Article)
	fmt.Println(author) //{1 张三 30 男 [{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]}
}

Preload

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联查询:

	//Preload方式查询:
	var author demostruct.Author
	//查询a_id=1的数据放入author中,并关联查询到Article字段对应的数据
	db.Preload("Article").Find(&author, "a_id = ?", 1)
	fmt.Println(author) //{1 张三 30 男 [{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]}
}
go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联查询:

	//Preload方式查询:
	//var author demostruct.Author
	//查询a_id=1的数据放入author中,并关联查询到Article字段对应的数据
	//db.Preload("Article").Find(&author,"a_id = ?",1)
	//fmt.Println(author)//{1 张三 30 男 [{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]}
	//Related方式查询:
	var author demostruct.Author
	db.First(&author, "a_id = ?", 1)
	fmt.Println(author) //{1 张三 30 男 []}
	var as []demostruct.Article
	//通过author模型查出来的Article字段的信息放入新的容器as中:
	db.Model(&author).Related(&as, "Article")
	fmt.Println(as)     //[{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]
	fmt.Println(author) //{1 张三 30 男 []}
}

关联更新

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联更新:
	//先查询
	//Preload方式查询:
	var author demostruct.Author
	//查询a_id=1的数据放入author中,并关联查询到Article字段对应的数据
	db.Preload("Article").Find(&author, "a_id = ?", 1)
	fmt.Println(author) //{1 张三 30 男 [{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]}
	//再更新:
	//如果直接Update操作那么关联的文章的记录就会被全部更改
	//db.Model(&author.Article).Update("title","JS入门")
	//所以你要改动指定的记录必须加入限定条件:
	db.Model(&author.Article).Where("ar_id = ?", 1).Update("title", "JS入门")
}

关联删除

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联删除:
	//先查询
	//Preload方式查询:
	var author demostruct.Author
	//查询a_id=1的数据放入author中,并关联查询到Article字段对应的数据
	db.Preload("Article").Find(&author, "a_id = ?", 1)
	fmt.Println(author) //{1 张三 30 男 [{1 HTML入门 HTML****** 好的不得了 1} {2 CSS入门 CSS****** 好的不得了2 1}]}
	//再删除:必须加入指定条件
	db.Where("ar_id = ?", 2).Delete(&author.Article)
}

多对多操作

使用上面多对多的创建表的代码先创建数据表

关联添加

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//关联添加操作:关联关系在Student中,所以我们模型操作的也是Student:
	stu := demostruct.Student{
		SNo:  1001,
		Name: "丽丽",
		Sex:  "女",
		Age:  18,
		Course: []demostruct.Course{
			{
				CName:       "c++",
				TeacherName: "张三",
				Room:        "s-103",
			},
			{
				CName:       "高数",
				TeacherName: "李四",
				Room:        "s-801",
			},
		},
	}
	db.Create(&stu)
}

事务处理

用 db.Begin() 声明开启事务,结束的时候调用 tx.Commit(),异常的时候调用 tx.Rollback()

常用方法

使用上面一对一操作里面的结构体和关联添加创建完数据表和字段后再看下面的代码

【1】First:按照条件查询,并且升序排列,查询出一条记录

【2】FirstOrCreate:有数据就查询出来,没有就创建一条记录

【3】Last:按照条件查询,并且降序排列,查询出一条记录

【4】Take:按照条件查询,查询出一条记录

【5】Find:按照条件查询

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//First:
	var user demostruct.User
	//First:SELECT * FROM `users`  WHERE (`users`.`user_id` = 1) ORDER BY `users`.`user_id` ASC LIMIT 1
	//db.Debug().First(&user,1) -->默认情况查询的是主键
	//fmt.Println(user)
	//对应SQL:SELECT * FROM `users`  WHERE (user_id = 1) ORDER BY `users`.`user_id` ASC LIMIT 1
	//db.Debug().First(&user,"user_id = ?",1)
	//fmt.Println(user)
	//对应SQL:SELECT * FROM `users`  WHERE (user_id = 1) ORDER BY `users`.`user_id` ASC LIMIT 1
	//db.Debug().Where("user_id = ?",1).First(&user)
	//fmt.Println(user)

	//FirstOrCreate
	//user2 := demostruct.User{//这里定义的结构体的实例的数值其实就是FirstOrCreate的查询条件
	//	UserId: 2,
	//	Age:    20,
	//	Name:   "菲菲",
	//	IID:    1,
	//}
	//如果有对应的数据,就查询出来,如果没有对应的数据,就会帮我们创建新的记录
	//db.FirstOrCreate(&user,user2)
	//fmt.Println(user)
	//Last:对应SQL:SELECT * FROM `users`  WHERE (`users`.`user_id` = 1) ORDER BY `users`.`user_id` DESC LIMIT 1
	//db.Debug().Last(&user,1)
	//fmt.Println(user)
	//Take:对应SQL: SELECT * FROM `users`  WHERE (`users`.`user_id` = 1) LIMIT 1
	//db.Debug().Take(&user,1)
	//fmt.Println(user)
	//Find:对应SQL:SELECT * FROM `users`  WHERE (`users`.`user_id` = 1)
	user_id_arr := []int{1, 2}
	db.Debug().Find(&user, user_id_arr) //SELECT * FROM `users`  WHERE (`users`.`user_id` IN (1,2))
	fmt.Println(user)
}

【6】Where:加入指定条件:具体条件为:=,like,in,and,between....

【7】Select:筛选查询出来的字段

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	var user demostruct.User
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//db.Debug().Where("user_id = ?",1).First(&user)
	//fmt.Println(user)
	//db.Debug().Where("user_id in (?)",[]int{1,2}).First(&user)
	//fmt.Println(user)
	db.Debug().Select("name,age").Where("user_id = ?", 1).First(&user)
	fmt.Println(user) //{0 19 丽丽 0}
}

【8】Create:添加数据

【9】Save:添加数据

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Create操作只可以插入一条技能,不能批量操作
	//user := demostruct.User{
	//	Age:    26,
	//	Name:   "小明",
	//	IID:    1,
	//}
	//
	//db.Create(&user)
	user := demostruct.User{
		Age:  14,
		Name: "莎莎",
		IID:  1,
	}
	db.Save(&user)
}

【10】Update:更新数据

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//更新:先查询再更新:
	var user demostruct.User
	//(1)先查询,再通过Model进行操作,再Update操作:
	//db.Where("user_id = ?",1).First(&user)
	//db.Model(&user).Update("age",29)
	//(2)直接在查询之后进行操作:
	db.Where("user_id = ?", 1).First(&user).Update("name", "露露")

	//(3)直接在查询之后进行操作,传入结构体示例,更新多个字段
	db.Where("user_id = ?", 1).First(&user).Update(demostruct.User{
		Age:  11,
		Name: "小刚",
	})
	//(4)直接在查询之后进行操作,传入map,更新多个字段
	db.Where("user_id = ?", 1).First(&user).Update(map[string]interface{}{
		"age":  21,
		"name": "小花",
	})
}

【11】Delete:删除数据

go
package main

import (
	"demo01/demostruct"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Delete:删除数据:
	// (1)先查询再删除:
	//var user demostruct.User
	//db.Where("user_id = ?",1).First(&user)
	//db.Delete(&user)
	//(2)通过条件直接进行删除:
	var user demostruct.User
	db.Where("user_id = ?", 2).Delete(&user)
}

【12】Not:排除某个具体条件的查询操作

【13】Or:多个条件的查询

【14】Order:进行升序或者降序的排列

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Not:
	//var users []demostruct.User
	//db.Not("user_id = ?",1).Find(&users)
	//fmt.Println(users)
	//对应SQL:SELECT * FROM `users`  WHERE (`users`.`age` <> 18) AND (`users`.`name` <> '丽丽')
	//var users []demostruct.User
	//db.Debug().Not(demostruct.User{
	//	Age:    18,
	//	Name:   "丽丽",
	//}).Find(&users)
	//fmt.Println(users)
	//Or :
	//var users []demostruct.User
	//db.Where("user_id = ?",1).Or("user_id = ?",3).Find(&users)
	//fmt.Println(users)
	//Order:
	var users []demostruct.User
	db.Where("age = ?", 14).Order("user_id asc").Find(&users)
	fmt.Println(users)
}

【15】Limit:指定获取记录的最大数量

【16】Offset:设置偏移

【17】Scan:将结果扫描到另一个结构体中

go
package main
import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)
func main() {
	//连接数据库:
	db,err := gorm.Open("mysql","root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Limit:
	//var users []demostruct.User
	//db.Limit(2).Find(&users)
	//fmt.Println(users)
	//Offset:
	//注意:Offset中设置的偏移数字为第几条记录,从0开始,0、1、2、、、、
	//注意:Offset必须和Limit结合使用
	//var users []demostruct.User
	//db.Offset(1).Limit(2).Find(&users)
	//fmt.Println(users)
	//Scan
	type UserDemo struct {//你要扫描的结构体的字段的名字和User中的字段名字必须一致才可以扫描
		Name1 string
		Age int
	}
	var userdemo UserDemo
	var user demostruct.User
	db.Where("user_id=?",1).Find(&user).Scan(&userdemo)
	fmt.Println(user)
	fmt.Println(userdemo)
}

【18】Count:计数

【19】GROUP:进行分组

【20】Having:分组后进行过滤

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Count:
	//var users []demostruct.User
	//定义一个变量接收计数的数量:
	//var count int
	//db.Find(&users).Count(&count)
	//fmt.Println(users)
	//fmt.Println(count)
	//Group:
	var users []demostruct.User
	//定义一个新的结构体:
	type GroupData struct {
		Age   int
		Count int
	}
	var group_date []GroupData
	//对应SQL:SELECT age,count(*) FROM `users`   GROUP BY age
	//对应SQL:SELECT age,count(*) as count FROM `users`   GROUP BY age HAVING (age > 18)
	//Having:在分组以后进行过滤
	db.Debug().Select("age,count(*) as count").Group("age").Find(&users).Having("age > 18").Scan(&group_date)
	fmt.Println(users)
	fmt.Println(group_date)

}

【21】Join :左连接、右连接:

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()

	//Joins:
	//定义一个新的结构体用于Scan:
	type NewUserInfo struct {
		User_Id int
		Name    string
		I_Id    int
		Info_Id int
		Address string
	}
	var newUser []NewUserInfo
	var users []demostruct.User
	db.Debug().Select("users.user_id,users.name,users.i_id,user_infos.info_id,user_infos.address").Joins("left join user_infos on users.i_id = user_infos.info_id").Find(&users).Scan(&newUser)
	fmt.Println(users)
	fmt.Println(newUser)
}

【22】LogMod: Gorm内置的日志记录器,显示详细日志

PS :利用Debug只能逐条打印对应日志信息,但是设置LogMod(true)相当于设置了全局打印,所有执行的逻辑日志都会帮我们输出打印

go
package main

import (
	"demo01/demostruct"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//开启打印日志:
	db.LogMode(true)

	//Joins:
	//定义一个新的结构体用于Scan:
	type NewUserInfo struct {
		User_Id int
		Name    string
		I_Id    int
		Info_Id int
		Address string
	}
	var newUser []NewUserInfo
	var users []demostruct.User
	db.Select("users.user_id,users.name,users.i_id,user_infos.info_id,user_infos.address").Joins("left join user_infos on users.i_id = user_infos.info_id").Find(&users).Scan(&newUser)
	fmt.Println(users)
	fmt.Println(newUser)
}

支持原生sql

go
package main

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//开启打印日志:
	db.LogMode(true)
	//查询操作:Raw
	//var users []demostruct.User
	//db.Raw("select * from users where age = ?",14).Find(&users)
	//fmt.Println(users)
	//增加、删除、修改 :Exec
	db.Exec("insert into users (age,name) values (?,?)", 33, "莹莹")
	//db.Exec("delete from users where user_id = ?",1)
	//db.Exec("update users set name = ? where user_id = ?","明明",3)
}

日志的引入

概述

【1】日志的重要性及作用: 日志是程序的重要组成部分 1.记录用户操作的审计日志 2.快速定位问题的根源 3.追踪程序执行的过程。 4.追踪数据的变化 5.数据统计和性能分析 6.采集运行环境数据 . 【2】第三方日志库: Golang标准库的日志框架非常简单,仅仅提供了print,panic和fatal三个函数,对于更精细的日志级别、日志文件分割以及日志分发等方面并没有提供支持。 所以催生了很多第三方的日志库,但是在golang的世界里,没有一个日志库像slf4j那样在Java中具有绝对统治地位。golang中,流行的日志框架包括logrus、zap、zerolog、seelog等。 logrus是目前Github上star数量最多的日志库,logrus功能强大,性能高效,而且具有高度灵活性,提供了自定义插件的功能。很多开源项目,如docker,prometheus等,都是用了logrus来记录其日志。

Logrus的使用

【1】下载Logrus第三方库: 录入安装Logrus的命令:

go
go get -u github.com/sirupsen/logrus

【2】单独提取配置文件:

根目录下创建confs/log_config.json文件,内容如下:

json
{
  "log_dir": "d:mylog.log",
  "log_level": "info"
}

【3】加载配置文件:

根目录下创建logs_ope/log_load_conf.go文件,内容如下:

go
package logs_ope
import (
        "encoding/json"
        "io/ioutil"
        "os"
)
//对应结构体:
type LogConfig struct {
        LogDir string `json:"log_dir"`
        LogLevel string  `json:"log_level"`
}
//读取配置文件:
func LoadLogConfig() *LogConfig{
        log_conf := LogConfig{}
        //打开文件:
        file,err := os.Open("confs/log_config.json")
        if err != nil{//错误处理
                panic(err)
        }
        //资源释放:
        defer file.Close()
        //用流读取文件中内容:
        data,err2  := ioutil.ReadAll(file)
        if err2 != nil {
                panic(err2)
        }
        //Unmarshal将json字符串解码到对应的数据结构中:
        //第一个参数:json字符串,第二个参数:接收json解析的数据结构
        err3 := json.Unmarshal(data,&log_conf)
        if err3 != nil {
                panic(err3)
        }
        return &log_conf
}

【4】初始化日志记录器实例:

新建logs_ope/log_init.go文件,内容如下:

go
package logs_ope
import (
	"github.com/sirupsen/logrus"
	"os"
)
//初始化记录器一个实例:
var Logrus = logrus.New()
func init(){
	//先读取日志的配置文件:
	log_conf := LoadLogConfig()
	//设置日志的输出文件:
	file,err := os.OpenFile(log_conf.LogDir,os.O_APPEND|os.O_CREATE,0666)
	if err != nil {
		panic(err)
	}
	//将上面打开的file文件设置为  日志的输出文件:
	Logrus.Out = file
	//设置日志的级别:
	//定义一个map,专门存储日志级别:
	log_level_map := map[string]logrus.Level{
		"trace" : logrus.TraceLevel,
		"panic": logrus.PanicLevel,
		"fatal": logrus.FatalLevel,
		"error": logrus.ErrorLevel,
		"warn": logrus.WarnLevel,
		"info": logrus.InfoLevel,
		"debug": logrus.DebugLevel,
	}
	Logrus.SetLevel(log_level_map[log_conf.LogLevel])
	//日志格式化:设置文本格式
	Logrus.SetFormatter(&logrus.TextFormatter{})
}

【5】在main.go中加载init函数:

go
package main

import (
	_ "demo01/logs_ope"
)

func main() {
}

【6】在具体的逻辑中可以使用日志记录器:

在根目录下创建stuope/stuope.go,内容如下:

go
package main

import (
	"demo01/logs_ope"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
	//连接数据库:
	db, err := gorm.Open("mysql", "root:admin@tcp(localhost:3306)/testgorm?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err) //如果出错,后续代码没有必要执行,想让程序中断,panic来执行即可
	}
	//数据库资源释放:
	defer db.Close()
	//开启打印日志:
	db.LogMode(true)

	// 日志记录操作
	logs_ope.Logrus.Info("向数据库中增加了一条记录")
}

运行项目,查看本地对应的日志信息打印