初次接触
原生SQL驱动包进行查询
gorm 操作mysql
数据库表:
-- ---------------------------- -- Table structure for users -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) NOT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of users -- ---------------------------- INSERT INTO `users` VALUES (1, '琳儿'); INSERT INTO `users` VALUES (2, '长路'); INSERT INTO `users` VALUES (3, '小明'); SET FOREIGN_KEY_CHECKS = 1;
代码:
package main import ( "database/sql" //导入sql驱动 "fmt" _ "github.com/go-sql-driver/mysql" "log" ) type User struct { id int64 name string } func main() { //使用driver(指定mysql) + DSN初始化DB连接 db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/hello") //rows, err := db.Query("select id,name from users where id = ?", 1) rows, err := db.Query("select id,name from users") if err != nil { log.Printf("连接异常") } defer func() { err = rows.Close() //处理完毕,释放连接 }() //定义用户数组 var users []User for rows.Next() { var user User err := rows.Scan(&user.id, &user.name) if err != nil { log.Printf("解析有误") } users = append(users, user) } fmt.Println(users) }
实战gorm查询单表所有记录
GORM增删改查、gorm普通的增删改查
数据库表:
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for food -- ---------------------------- DROP TABLE IF EXISTS `food`; CREATE TABLE `food` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID,商品Id', `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名', `price` decimal(10, 2) UNSIGNED NOT NULL COMMENT '商品价格', `type_id` int(10) UNSIGNED NOT NULL COMMENT '商品类型Id', `createtime` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of food -- ---------------------------- INSERT INTO `food` VALUES (1, 'CHANGLU', 100.00, 125, '2022-05-10 16:01:04'); INSERT INTO `food` VALUES (2, 'LINER', 1000.00, 666, '2022-05-03 16:01:25'); SET FOREIGN_KEY_CHECKS = 1;
代码:
package main import ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "log" "testing" "time" ) //默认gorm对struct字段名使用Snake Case命名风格转换成mysql表字段名(需要转换成小写字母) type Food struct { Id int //表字段名为:id Name string //表字段名为:name Price float64 //表字段名为:price TypeId int //表字段名为:type_id //我们也可以进行自定义对应映射的字段名:字段定义后面使用两个反引号``包裹起来的字符串部分叫做标签定义,这个是golang的基础语法,不同的库会定义不同的标签,有不同的含义 CreateTime time.Time `gorm:"column:createtime"` //表字段名为:createtime //若是数据库中是DateTime,那么对应这里使用time,之后序列化可以进行转换时间日期 } //创建表默认是这个表名 //设置表名,可以通过给Food struct类型定义 TableName函数,返回一个字符串作为表名 func (v Food) TableName()string { return "food" } func Test_gormImpl(t *testing.T) { db, err := connect() if err != nil { log.Printf("") } //v2版本中db没有close()方法:https://www.cnblogs.com/chengqiang521/p/15122102.html sqlDB, _ := db.DB() var foods []Food //查询所有的food列表记录 db.Find(&foods) fmt.Println(foods) //延时关闭数据库连接 defer sqlDB.Close() } func connect() (db *gorm.DB, err error) { //配置MySQL连接参数 username := "root" //账号 password := "123456" //密码 host := "127.0.0.1" //数据库地址,可以是Ip或者域名 port := 3306 //数据库端口 Dbname := "hello" //数据库名 timeout := "10s" //连接超时,10秒 //拼接下dsn参数, dsn格式可以参考上面的语法,这里使用Sprintf动态拼接dsn参数,因为一般数据库连接参数,我们都是保存在配置文件里面,需要从配置文件加载参数,然后拼接dsn。 dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&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 { return db, err } return db,nil }
一、知识点学习
1.1、gorm.Model
type Model struct { ID uint `gorm:"primarykey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt DeletedAt `gorm:"index"` } //其中DeletedAt的类型为sql.NullTime //该属性涉及到之后软删除和硬删除 type DeletedAt sql.NullTime
1.2、CRUD大整合
配套代码:go-gormLearn/***
说明:我们只要建好数据库即可,其他表都可以进行自动生成。
init.go: package db import ( "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) var DB *gorm.DB func Init() { var err error DB, err = gorm.Open(mysql.Open("root:123456@tcp(localhost:3306)/hello?charset=utf8&parseTime=True&loc=Local"), &gorm.Config{ PrepareStmt: true, SkipDefaultTransaction: true, Logger:logger.Default.LogMode(logger.Info), //打印执行的sql }, ) if err != nil { panic(err) } //进行表的创建 m := DB.Migrator() if !m.HasTable(&User{}) { if err = m.CreateTable(&User{}); err != nil { panic(err) } } }
核心的增删改查方法在user.go中:下面的内容都在这个user.go中
package db import ( "context" "gorm.io/gorm" ) type User struct { gorm.Model Name string `gorm:"type:varchar(32);not null"` Password string `gorm:"type:varchar(32);not null"` } func (v User) TableName()string { return "user" }
添加
//创建用户 func CreateUser(ctx context.Context, users []*User) error { //INSERT INTO `user` (`created_at`,`updated_at`,`deleted_at`,`name`,`password`) VALUES ('2022-05-28 19:30:45.339','2022-05-28 19:30:45.339',NULL,'changlu','123456'),() return DB.WithContext(ctx).Create(users).Error }
//查询用户(未被软删除的) func QueryAllUsers(ctx context.Context)([]*User, error) { var users []*User //SELECT * FROM `user` WHERE `user`.`deleted_at` IS NULL if err := DB.WithContext(ctx).Find(&users).Error; err != nil { return nil,err } return users, nil } //查询所有用户(包含软删除的) func QueryAllIncludeDeletedUsers(ctx context.Context)([]*User, error) { var users []*User //SELECT * FROM `user` if err := DB.WithContext(ctx).Unscoped().Find(&users).Error; err != nil { return nil,err } return users, nil } //只查询软删除用户 func QueryAllDeletedUsers(ctx context.Context)([]*User, error) { var users []*User //SELECT * FROM `user` WHERE deleted_at <> '' if err := DB.WithContext(ctx).Unscoped().Where("deleted_at <> ?", "").Find(&users).Error;err != nil { return nil, err } return users, nil } //分页查询 func PageQueryUser(ctx context.Context, pageNo, pageSize int)([]*User, error) { tx := DB.WithContext(nil).Model(&User{}) //...字段补充 tx.where()... var users []*User if err := tx.Offset((pageNo - 1) * pageSize).Limit(pageSize).Find(&users).Error; err != nil { return nil, err } return users, nil }
两种方式修改:①struct更新。(非零不会更新)②select更新。(必更新)
当通过 struct 更新时,GORM 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作:
//修改用户 //方式一:通过struct来进行更新 func UpdateUser(ctx context.Context, user *User) error { //UPDATE `user` SET `updated_at`='2022-05-28 19:31:24.468',`name`='changlu111',`password`='12132232' WHERE `user`.`deleted_at` IS NULL AND `id` = 1 return DB.WithContext(ctx).Model(&user).Select("Name", "Password").Updates(user).Error } //方式二:通过map进行更新 func UpdateUser2(ctx context.Context, ID uint, username *string, password *string )error { params := make(map[string]interface{}) if username != nil { params["Name"] = username } if password != nil { params["Password"] = password } //UPDATE `user` SET `name`='changlu666',`password`='122222',`updated_at`='2022-05-28 19:32:05.675' WHERE id = 1 AND `user`.`deleted_at` IS NULL return DB.WithContext(ctx).Model(&User{}).Where("id = ?", ID).Updates(params).Error } //恢复软删除 func RecoverUserDeleted(ctx context.Context, ID uint)error { //UPDATE `user` SET `deleted_at`=NULL,`updated_at`='2022-05-28 19:16:19.579' WHERE `ID` = 1 return DB.WithContext(ctx).Model(&User{}). Unscoped(). //针对已软删除的,此时就不会带上`user`.`deleted_at` IS NULL Where("ID", ID).Update("deleted_at", nil).Error }
删
gorm软删除妙用-充值,软删除恢复,soft delete
//删除用户 //方式一:软删除 func SofeDeleteUser(ctx context.Context, ID uint) error { //uint指的是无符号整数,其大小根据当前的平台来决定 //UPDATE `user` SET `deleted_at`='2022-05-28 19:32:30.124' WHERE id = 1 AND `user`.`deleted_at` IS NULL return DB.WithContext(ctx).Where("id = ?", ID).Delete(&User{}).Error } //方式二:硬删除 func DeleteUser(ctx context.Context, user *User) error { //调用Unscoped即可表示硬删除 //DELETE FROM `user` WHERE `user`.`id` = 1 return DB.WithContext(ctx).Unscoped().Delete(user).Error }
main.go:测试方法
package main import ( "fmt" "go-gormLearn/db" ) func main() { db.Init() //1、创建用户(无需填充gorm.model,即可自动进行插入更新值) //users := make([]*db.User, 2) //users[0] = &db.User{ // Model: gorm.Model{}, // Name: "changlu", // Password: "123456", //} //users[1] = &db.User{ // Model: gorm.Model{}, // Name: "test", // Password: "123456", //} //db.CreateUser(nil, users) //2、更新用户(会自动更新updatetime) //方式一:借助struct //u := &db.User{ // Model: gorm.Model{ // ID: 1, // }, // Name: "changlu111", // Password: "12132232", //} //db.UpdateUser(nil, u) //通过! //方式二:借助map //username := "changlu666" //password := "122222" //db.UpdateUser2(nil, 1, &username, &password)//通过! //恢复软删除 db.RecoverUserDeleted(nil, 1) //3、删除用户 //3.1、软删除 //db.SofeDeleteUser(nil, 1) //3.2、硬删除 //db.DeleteUser(nil, &db.User{ // Model: gorm.Model{ // ID: 1, // }, //}) //4、查询用户 //4.1、查询所有未软删除用户 //users, _ := db.QueryAllUsers(nil) //for _, user := range users { // fmt.Println(user) //} //4.2、查询所有用户(包含软删除的) //users, _ := db.QueryAllIncludeDeletedUsers(nil) //for _, user := range users { // fmt.Println(user) //} //4.3、只查询软删除的用户 //users, _ := db.QueryAllDeletedUsers(nil) //for _, user := range users { // fmt.Println(user) //} //4.4、分页查询 users, _ := db.PageQueryUser(nil, 1, 1) for _, user := range users { fmt.Println(user) } }
二、性能提升
2.1、基本配置
gorm中文文档—事务
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ ... //配置信息 })
①禁止默认事务:对于写操作(创建、更新、删除),为了确保数据的完整性,GORM 会将它们封装在事务内运行。但这会降低性能,你可以在初始化时禁用这种方式
SkipDefaultTransaction: true,
②缓存预编语句:执行任何 SQL 时都创建并缓存预编译语句,可以提高后续的调用速度
全局模式:
PrepareStmt: true, //【若是使用hock或者关联创建的时候设置为false】
会话模式:
// 会话模式 tx := db.Session(&Session{PrepareStmt: true}) tx.First(&user, 1) tx.Find(&users) tx.Model(&user).Update("Age", 18)
③Prepared Statement与原生sql使用
PrepareStmt: true,
//示例: db.Raw("select sum(age) from users where role = ?", "admin").Scan(&age) 1 2 3 4 2.2、常用配置 import ( "gorm.io/driver/mysql" "gorm.io/gorm" ) var DB *gorm.DB func Init() { var err error DB, err = gorm.Open(mysql.Open("gorm:gorm@tcp(localhost:3306)/hello?charset=utf8&parseTime=True&loc=Local"), &gorm.Config{ PrepareStmt: true, //预编译 SkipDefaultTransaction: true, //关闭默认事务 }, ) if err != nil { panic(err) } }
三、Migrator
gorm的Migrator是干什么的?
常用操作:表的创建,核心是用来进行表结构操作的。Gorm之gorm.Migrator接口详解
//进行表的创建 m := DB.Migrator() if !m.HasTable(&User{}) { //是否存在表 if err = m.CreateTable(&User{}); err != nil { //创建表 panic(err) } }
问题1:为什么设置的结构体的某个字段没有再表中创建?
原因:字段必须为大写,否则无法创建。
参考:关于 gorm AutoMigrate 不自动创建字段的解决方案 type User struct { gorm.Model Name string `gorm:"type:varchar(32);not null"` //使用gorm可以来进行定义字段的类型 Password string `gorm:"type:varchar(32);not null"` }
四、其他方面
4.1、执行sql时打印日志
如何让gorm输出执行的sql
1、打印所有的sql
&gorm.Config{ Logger:logger.Default.LogMode(logger.Info), //打印执行的sql },