【Java转Go】快速上手学习笔记(五)之Gorm篇

目录

  • go get命令
    • 1、go get命令无响应问题
    • 2、Unresolved dependency错误
  • 连接数据库
    • 连接.go
    • main.go
  • 操作数据库
    • 创建表
    • 新增数据
    • 更新数据
    • 删除数据
    • 查询数据
      • 单表查询
      • 多表查询
    • 用到的数据库表
    • 原生SQL
  • 完整代码

go往期文章笔记:

【Java转Go】快速上手学习笔记(一)之环境安装篇

【Java转Go】快速上手学习笔记(二)之基础篇一

【Java转Go】快速上手学习笔记(三)之基础篇二

【Java转Go】快速上手学习笔记(四)之基础篇三


这篇我们来讲讲Go中的orm框架:Gorm

首先我们要先去下载gorm和mysql依赖。

go get命令

1、go get命令无响应问题

例如我下载gorm

go get -u gorm.io/gorm

控制台输出服务器没有响应,连接失败的提示

go: module gorm.io/gorm: Get "https://proxy.golang.org/gorm.io/gorm/@v/list": dial tcp 172.217.163.49:443: connectex: A connection attempt failed because the connected party did not properly respond aftera period of time, or established connection failed because connected host has failed to respond.

这个时候我们需要设置代理,打开cmd窗口,输入:

go env -w GOPROXY=https://goproxy.cn

执行完后,再去执行go get命令,就可以下载了。

2、Unresolved dependency错误

下载完之后,我们发现在go.mod文件里,require中列出的项目依赖报错,鼠标放上去,提示:Unresolved dependency(未解决的依赖关系)

这时,我们在 File-->Setting ,然后搜索 Go Modules ,勾选 Enable Go modules integration 然后再点击 ok 就可以了。

在这里插入图片描述
设置好后,Gorm依赖就可以正确引入了,那么接下来就是下载数据库的依赖

// mysql 的依赖
go get gorm.io/driver/mysql// sqlite的依赖
// go get -u gorm.io/driver/sqlite

下载好依赖后,那么go.mod文件的配置应该是

module go-web1go 1.21require (github.com/go-sql-driver/mysql v1.7.0 // indirectgithub.com/jinzhu/inflection v1.0.0 // indirectgithub.com/jinzhu/now v1.1.5 // indirectgorm.io/driver/mysql v1.5.1 // indirectgorm.io/gorm v1.25.3 // indirect
)

连接数据库

引入了mysql依赖,接下来我们就可以来连接数据库了。我们先创建一个go文件,用来连接数据库的

连接.go

package sqlimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""gorm.io/gorm/schema"
)// 声明全局DB变量,后续可以用这个来操作数据库
var DB *gorm.DB// 数据库连接
func Init() {username := "root"   //账号password := "root"   //密码host := "127.0.0.1"  //数据库地址,可以是Ip或者域名port := 3306         //数据库端口Dbname := "gorm_demo" //数据库名timeout := "10s"     //连接超时,10秒// sql全局日志var sqlLogger logger.InterfacesqlLogger = 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{// 跳过默认事务:为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这样可以获得60%的性能提升//SkipDefaultTransaction: true,Logger: sqlLogger,NamingStrategy: schema.NamingStrategy{TablePrefix:   "sys_", // 表名前缀SingularTable: true,   // 单数表名NoLowerCase:   false,  // 关闭小写转换},})if err != nil {panic("连接数据库失败, error=" + err.Error())}DB = db
}

写好了连接,接下来就是测试连接是否成功

main.go

package mainimport ("fmt""go-demo1/sql"
)func main() {sql.Init()fmt.Println("连接成功:", sql.DB)
}

操作数据库

数据库连上了,就可以来操作数据库了。

创建表

创建表的话,我们需要一个和数据库表对应的结构体。

/*
字段标签:`gorm:""` 格式,然后在双引号里面设置,多个标签用分号 ; 分隔。type 定义字段类型size 定义字段大小column 自定义列名primaryKey 将列定义为主键unique 将列定义为唯一键default 定义列的默认值not null 不可为空embedded 嵌套字段embeddedPrefix 嵌套字段前缀comment 注释
*/
// 创建一个gorm测试结构体
type Gorm struct {ID         uint      `gorm:"type:bigint;common:主键"` // 默认使用ID作为主键,gorm会自动识别 ”ID“Name       string    `gorm:"type:varchar(20);not null;common:名称"`DeptId     int       `gorm:"column:dept_id;type:bigint;common:部门id"`DeptName   string    `gorm:"-"`                           // gorm:"-" 表示忽略和数据库表字段的映射,相当于Java里的 transient 或 @TableField(exist = false)Email      *string   `gorm:"type:varchar(100);common:邮箱"` // 这里用指针是为了存null值,而不是空字符串State      int       `gorm:"type:tinyint(1);common:状态(1 正常 2 禁用)"`CreateTime time.Time `gorm:"type:datetime;common:创建时间;default:CURRENT_TIMESTAMP"`
}// 创建一个role(角色)测试结构体
type Role struct {ID         uintName       stringKey        stringRemark     *stringCreateTime time.Time
}// 创建一个dept(部门)测试结构体
type Dept struct {ID         uintName       stringParentId   intLevel      intSort       intCreateTime time.Time
}type Test struct {Name     stringDeptId   intDeptName stringEmail    string
}// 存放分组查询结果
type Group struct {DeptId int    // 以部门id进行分组Count  int    `gorm:"column:count(id)"`Name   string `gorm:"column:group_concat(name)"`
}func main() {sql.Init()// 可以放多个sql.DB.AutoMigrate(&Gorm{})
}

新增数据

main 函数 里面直接调用 insert() 函数即可

// 数据添加
func insert() {// 单条数据添加user := Gorm{Name:   "符华",DeptId: 1,State:  1,}// Create接收的是一个指针,而不是值sql.DB.Create(&user)// 批量新增,新增10条数据var gormList []Gormfor i := 0; i < 10; i++ {email := "jiqiren" + fmt.Sprintf("%d", i) + "@qq.com"gormList = append(gormList, Gorm{Name:   fmt.Sprintf("机器人%d号", i+1),DeptId: 3,State:  2,Email:  &email, // 指针类型是为了更好的存null类型,但是传值的时候,也记得传指针})}sql.DB.Create(&gormList)
}

更新数据

// 数据更新
func update() {var user Gormemail := "fuhua@163.com"user.Email = &email//更新指定字段sql.DB.Model(&Gorm{}).Where("name", "符华").Update("name", "云墨")sql.DB.Model(&Gorm{}).Where(1).Updates(Gorm{Name: "符华", Email: &email}) // 是结构体,默认不会更新零值sql.DB.Model(&Gorm{}).Where("name = ?", "机器人1号").Updates(Gorm{DeptId: 4, Email: &email, State: 1})// Save()会更新全部字段,即使字段值是零值也会更新sql.DB.Save(&user)                        // 更新全部字段sql.DB.Select("email").Save(&user) // 如果不想更新所有字段,可以通过Select指定要更新的字段// Omit() 可以忽略字段
}

删除数据

// 数据删除
func delete() {var user Gorm//根据主键ID删除sql.DB.Delete(&user, 11) // DELETE FROM `sys_gorm` WHERE `sys_gorm`.`id` = 1sql.DB.Delete(&Gorm{}, []int{1,2,3})// 根据其他条件删除sql.DB.Where("name like ?", "%李四%").Delete(Gorm{}) // DELETE FROM `sys_gorm` WHERE name like '%李四%'sql.DB.Debug().Delete(Gorm{}, "name like ?", "%李四%") // DELETE FROM `sys_gorm` WHERE name like '%李四%'// 如果模型有DeletedAt字段,将自动获得软删除功能! 在调用Delete时不会从数据库中永久删除,而是只将字段DeletedAt的值设置为当前时间。软删除的记录将在查询时被忽略。//使用Unscoped查找软删除的记录//sql.DB.Unscoped().Where("name=?", "张三").Find(&user)// 使用Unscoped永久删除记录//sql.DB.Unscoped().Delete(&user)// 删除查询到的切片列表var gormList []Gormsql.DB.Delete(&gormList)
}

查询数据

单表查询

查询数据比较多,分为单表查询和多表查询,下面这个 query1() 是单表查询的方法。

除了基本的根据各条件查询,还有去重、分组、子查询和分页查询,然后还有可以把A结构体的查询结果存入B结构体,或者是直接把查询结果放入map中。

// 数据查询(单表)
func query1() {// 查询单条数据//var user Gorm//var test Test//sql.DB.Take(&user) // SELECT * FROM `sys_gorm` LIMIT 1//fmt.Println(user)//sql.DB.First(&user) // SELECT * FROM `sys_gorm` ORDER BY `id` LIMIT 1//fmt.Println(user)//sql.DB.Last(&user) // SELECT * FROM `sys_gorm` ORDER BY `id` DESC LIMIT 1//fmt.Println(user)// 根据主键查询,Take的第二个参数,默认会根据主键查询,可以是字符串,可以是数字//sql.DB.Take(&user, 2)//fmt.Println(user)//user = Gorm{} // 重新赋值//sql.DB.Take(&user, "4")//fmt.Println(user)// 根据其他条件查询//sql.DB.Take(&user, "name = ?", "机器人7号") // 使用 ? 号作为占位符,将查询内容放入 ?//sql.DB.Where(&Gorm{Name: "机器人10号"}).First(&user)//fmt.Println(user)// 根据struct查询//user.ID = 2//user.Name = "机器人1号"//sql.DB.Take(&user)//data, _ := json.Marshal(user) // 由于email是指针类型,所以看不到实际的内容,但是序列化之后,会转换为我们可以看得懂的方式//fmt.Println(string(data))//fmt.Println(user)// 通过 Scan 方法,可以将查询的结果存入另一个结构体中//sql.DB.Select("name", "dept_id").Find(&user).Scan(&test) // 这种方式会查询两次,不推荐使用//sql.DB.Model(&Gorm{}).Select("name", "dept_id").Scan(&test) // 这种方式就只查询一次了//sql.DB.Table("sys_gorm").Select("name", "dept_id").Scan(&test) // 这种方式也是只查询一次//fmt.Println(test)// 根据map查询//sql.DB.Where(map[string]any{"name": "符华", "dept_id": 1}).Find(&user)//fmt.Println(user)// 查询多条//var gormList []Gorm//var testList []Test//var count int64 // 获取查询结果的总数量//sql.DB.Find(&gormList)//for _, u := range gormList {//	data, _ := json.Marshal(u) // 序列化//	fmt.Println(string(data))//}// 根据多个主键查询//sql.DB.Not([]int64{4, 5, 6}).First(&gormList) // not in 主键//sql.DB.Find(&gormList, []int{1, 3, 5, 7})//sql.DB.Find(&gormList, []string{"1", "3", "5", "7"})//sql.DB.Find(&gormList, 1, 3, 5, 7) // 一样的//sql.DB.Find(&gormList, "1", "3", "5", "7")//fmt.Println(gormList)// 根据多个其他条件查询//sql.DB.Find(&gormList, "name in ?", []string{"符华", "机器人10号"})//sql.DB.Where("name in (?) ", []string{"机器人1号", "机器人9号"}).Find(&gormList) // in//sql.DB.Where("name<>?", "机器人10号").Find(&gormList) // 不等于//sql.DB.Where("name like ?", "%机器人%").Find(&gormList) // like//sql.DB.Where("name =? and email=?", "机器人10号", "jiqiren@qq.com").Find(&gormList) // and//sql.DB.Where("name =?", "机器人10号").Where("email=? ", "jiqiren@qq.com").Find(&gormList) // and//sql.DB.Where("name=?", "符华").Or("email=?", "jiqiren1@qq.com").Find(&gormList)         // or//sql.DB.Where(Gorm{Name: "符华"}).Or(Gorm{DeptId: 1}).Find(&gormList) // or//sql.DB.Not("name", "符华").Find(&gormList)                                              // not条件查询//sql.DB.Not("name", []string{"机器人1号", "机器人9号"}).Find(&gormList).Count(&count)       // not in//sql.DB.Debug().Order("id desc").Find(&gormList).Count(&count) // order by排序//fmt.Println("查询的总数量:", count)//fmt.Println("查询的结果集:", gormList)// 查询指定的列(使用Select会查询两次,分别用于获取指定的字段和获取完整的模型数据。)//sql.DB.Select("name", "email").Find(&gormList, "name in ?", []string{"符华", "机器人10号"}).Count(&count)//sql.DB.Select([]string{"name", "email"}).Find(&gormList) // 一样的效果//fmt.Println(count, gormList)// 使用原生sql查询//sql.DB.Raw("SELECT id, name FROM sys_gorm WHERE id = ?", 1).Find(&gormList)//fmt.Println(gormList)// 去重//var emailList []string//sql.DB.Table("sys_gorm").Select("email").Distinct("email").Scan(&emailList)//sql.DB.Table("sys_gorm").Select("distinct email").Scan(&emailList)//fmt.Println(emailList)// 分组查询//var idList []string//sql.DB.Table("sys_gorm").Select("count(id)").Group("dept_id").Scan(&idList) // 根据部门id进行分组//fmt.Println(idList)//var groupList []Group//sql.DB.Table("sys_gorm").Select("count(id)", "dept_id", "group_concat(name)").Group("dept_id").Scan(&groupList) // 根据部门名称进行分组//fmt.Println(groupList)// 查询结果查询到map中//var res []map[string]any//sql.DB.Table("sys_gorm").Find(&res)//fmt.Println(res)// 分页//pageSize := 5                      // 每次查询条数//pageNum := 2                       // 当前页//offset := (pageNum - 1) * pageSize // 计算跳过的记录数//sql.DB.Offset(offset).Limit(pageSize).Find(&gormList) // 分页查询,根据offset和limit来查询//sql.DB.Offset(offset).Limit(pageSize).Find(&gormList).Scan(&testList) // 通过Scan方法,将查询的结果集存入另一个list//sql.DB.Model(&Gorm{}).Count(&count)                                   // 获取总数量(这个数量不是每一页的数量)//fmt.Println("查询的总数量:", count)//fmt.Println("查询的结果集gormList:", gormList)//fmt.Println("查询的结果集testList:", testList)// 子查询//var gromList []Gorm// SELECT * FROM `sys_gorm` WHERE id in (SELECT `id` FROM `sys_gorm` WHERE `state` = 2)//sql.DB.Model(Gorm{}).Where("id in (?)", sql.DB.Model(Gorm{}).Select("id").Where("state", 2)).Find(&gromList)
}

多表查询

多表关联查询:根据grom表的deptId字段,关联dept表,然后还需要关联角色表查询(一个用户可以有多个角色)

Gorm结构体我们需要改一下:

// 创建一个gorm测试结构体
type Gorm struct {ID         uint      `gorm:"type:bigint;common:主键"` // 默认使用ID作为主键,gorm会自动识别 ”ID“Name       string    `gorm:"type:varchar(20);not null;common:名称"`DeptId     int       `gorm:"column:dept_id;type:bigint;common:部门id"`DeptName   string    `gorm:"-"`                           // gorm:"-" 表示忽略和数据库表字段的映射,相当于Java里的 transient 或 @TableField(exist = false)Email      *string   `gorm:"type:varchar(100);common:邮箱"` // 这里用指针是为了存null值,而不是空字符串State      int       `gorm:"type:tinyint(1);common:状态(1 正常 2 禁用)"`CreateTime time.Time `gorm:"type:datetime;common:创建时间;default:CURRENT_TIMESTAMP"`Dept       Dept      `gorm:"foreignKey:DeptId"` // 使用 foreignKey 并不会要求在数据库表里面有这个外键约束,这个标签仅仅是用来指定在进行关联查询时应该使用的外键字段的名字。Roles      []*Role   `gorm:"many2many:user_role;"`
}

Roles []*Role gorm:"many2many:user_role;" 这一句中的 user_role 就是用户和角色的关联关系表。

// 数据查询(多表关联)
func query2() {var gormList []Gorm// 关联查询部门//sql.DB.Preload("Dept").Find(&gormList)//for _, u := range gormList {//	fmt.Printf("用户 %v 所在部门是 %v\n", u.Name, u.Dept.Name)//}// 关联查询角色//var user Gorm//sql.DB.Preload("Roles").First(&user)//for _, role := range user.Roles {//	fmt.Println(user.Name, "的角色是", role.Name)//}// 多个关联sql.DB.Preload("Dept").Preload("Roles").Find(&gormList)for _, u := range gormList {var role stringfor i, r := range u.Roles {if i > 0 {role += ","}role += r.Name}fmt.Printf("用户 %v 所在部门是 %v,她的角色是 [%v]\n", u.Name, u.Dept.Name, role)}
}

用到的数据库表

DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept`  (`id` bigint NOT NULL,`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`parent_id` bigint NULL DEFAULT NULL COMMENT '父部门id',`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '部门名称',`level` tinyint(1) NULL DEFAULT NULL COMMENT '层级(1 根目录 2 单位 3 部门)',`sort` bigint NULL DEFAULT NULL COMMENT '序号',PRIMARY KEY (`id`) USING BTREE,INDEX `name`(`name` ASC) USING BTREE,INDEX `parent_id`(`parent_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门管理' ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_dept
-- ----------------------------
INSERT INTO `sys_dept` VALUES (1, '2023-08-18 15:39:57', 0, '太虚派', 1, 1);
INSERT INTO `sys_dept` VALUES (2, '2023-08-18 15:40:00', 0, '天命科技有限公司', 1, 2);
INSERT INTO `sys_dept` VALUES (3, '2023-08-18 15:40:57', 1, '心部', 2, 1);
INSERT INTO `sys_dept` VALUES (4, '2023-08-18 15:41:07', 1, '形部', 2, 2);
INSERT INTO `sys_dept` VALUES (5, '2023-08-18 15:44:19', 1, '意部', 2, 3);
INSERT INTO `sys_dept` VALUES (6, '2023-08-18 15:44:22', 1, '魂部', 2, 4);
INSERT INTO `sys_dept` VALUES (7, '2023-08-18 15:44:28', 1, '神部', 2, 5);
INSERT INTO `sys_dept` VALUES (8, '2023-08-18 15:44:58', 2, '天命总部', 2, 1);
INSERT INTO `sys_dept` VALUES (9, '2023-08-18 15:45:00', 2, '极东支部', 2, 2);-- ----------------------------
-- Table structure for sys_gorm
-- ----------------------------
DROP TABLE IF EXISTS `sys_gorm`;
CREATE TABLE `sys_gorm`  (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`dept_id` bigint NULL DEFAULT NULL,`email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`state` tinyint(1) NULL DEFAULT NULL,`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE,INDEX `DeptId`(`dept_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_gorm
-- ----------------------------
INSERT INTO `sys_gorm` VALUES (1, '符华', 1, 'fuhua@163.com', 1, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (2, '机器人1号', 3, 'jiqiren0@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (3, '机器人2号', 3, 'jiqiren1@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (4, '机器人3号', 3, 'jiqiren2@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (5, '机器人4号', 3, 'jiqiren3@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (6, '机器人5号', 3, 'jiqiren4@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (7, '机器人6号', 3, 'jiqiren5@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (8, '机器人7号', 3, 'jiqiren6@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (9, '机器人8号', 3, 'jiqiren7@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (10, '机器人9号', 3, 'jiqiren8@qq.com', 2, '2023-08-18 16:02:44');
INSERT INTO `sys_gorm` VALUES (11, '机器人10号', 3, 'jiqiren9@qq.com', 2, '2023-08-18 16:02:44');-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (`id` bigint NOT NULL COMMENT '角色ID',`key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色权限字符',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注',`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色管理' ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'CJGLY', '超级管理员', NULL, '2022-09-14 14:25:07');
INSERT INTO `sys_role` VALUES (2, 'PTYH', '普通用户', NULL, '2022-09-14 14:38:35');
INSERT INTO `sys_role` VALUES (3, 'BMFZR', '部门负责人', NULL, '2023-08-18 15:58:15');-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (`gorm_id` bigint NOT NULL,`role_id` bigint NOT NULL,PRIMARY KEY (`gorm_id`, `role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (1, 3);
INSERT INTO `sys_user_role` VALUES (2, 2);
INSERT INTO `sys_user_role` VALUES (3, 2);
INSERT INTO `sys_user_role` VALUES (4, 2);
INSERT INTO `sys_user_role` VALUES (5, 3);
INSERT INTO `sys_user_role` VALUES (6, 2);
INSERT INTO `sys_user_role` VALUES (7, 3);
INSERT INTO `sys_user_role` VALUES (8, 3);
INSERT INTO `sys_user_role` VALUES (9, 2);

原生SQL

关于原生sql

Go中,执行原生sql是用 Raw() 方法,然后它好像不支持从外部文件调用sql语句。(找了下没找到相关资料)

也就是说不能像Java那样,有专门的xml来放原生sql,然后用到的时候直接调用对应的方法就能执行方法里面的sql。(SQL和业务代码分离,易于维护)

如果只是较简单的sql还好,但是实际项目中,碰到的业务查询是不可能只有一两张表、两三张表关联查询就能解决的。

关联的表很多、子查询很多、逻辑复杂,往往需要通过原生sql来实现。

然后我目前想到一个方案,就是能不能将sql以字符串的形式,放到变量里,然后这些存放sql语句的变量,统一写在go文件中(相当于Java的一个xml)

  • 不同业务的go文件放在不同包中,比如:用户相关的放在user包里,部门相关放在dept包里(实际项目中,不同业务肯定是要分包的,不会全部放在一个包或一个文件中,不然的话会很乱)

  • 当我们需要用到某个sql,通过包名点出存放要执行的sql的变量,然后放到 Raw 中执行,这样是不是就可以实现 SQL和业务代码分离?

  • 比如:user 包下有个 sql.go,这个go里面有个存放查询用户列表sql的变量叫 queryUserList,当要查询用户列表时:db.Raw(user.queryUserList) 这样来实现。

当然以上只是我一个想法,因为我也是刚入门Go语言,不知道还没有更好的办法可以实现 SQL和业务代码分离

完整代码

main.go 的完整代码 ,大家可自行复制测试。

package mainimport ("fmt""go-demo1/sql""time"
)/*
字段标签:`gorm:""` 格式,然后在双引号里面设置,多个标签用分号 ; 分隔。type 定义字段类型size 定义字段大小column 自定义列名primaryKey 将列定义为主键unique 将列定义为唯一键default 定义列的默认值not null 不可为空embedded 嵌套字段embeddedPrefix 嵌套字段前缀comment 注释
*/
// 创建一个gorm测试结构体
type Gorm struct {ID         uint      `gorm:"type:bigint;common:主键"` // 默认使用ID作为主键,gorm会自动识别 ”ID“Name       string    `gorm:"type:varchar(20);not null;common:名称"`DeptId     int       `gorm:"column:dept_id;type:bigint;common:部门id"`DeptName   string    `gorm:"-"`                           // gorm:"-" 表示忽略和数据库表字段的映射,相当于Java里的 transient 或 @TableField(exist = false)Email      *string   `gorm:"type:varchar(100);common:邮箱"` // 这里用指针是为了存null值,而不是空字符串State      int       `gorm:"type:tinyint(1);common:状态(1 正常 2 禁用)"`CreateTime time.Time `gorm:"type:datetime;common:创建时间;default:CURRENT_TIMESTAMP"`Dept       Dept      `gorm:"foreignKey:DeptId"` // 使用 foreignKey 并不会要求在数据库表里面有这个外键约束,这个标签仅仅是用来指定在进行关联查询时应该使用的外键字段的名字。Roles      []*Role   `gorm:"many2many:user_role;"`
}// 创建一个role(角色)测试结构体
type Role struct {ID         uintName       stringKey        stringRemark     *stringCreateTime time.Time
}// 创建一个dept(部门)测试结构体
type Dept struct {ID         uintName       stringParentId   intLevel      intSort       intCreateTime time.Time
}type Test struct {Name     stringDeptId   intDeptName stringEmail    string
}// 存放分组查询结果
type Group struct {DeptId int    // 以部门id进行分组Count  int    `gorm:"column:count(id)"`Name   string `gorm:"column:group_concat(name)"`
}func main() {sql.Init()//fmt.Println("连接成功:", sql.DB)// 可以放多个//sql.DB.AutoMigrate(&Gorm{})//insert() // 新增数据//update() // 更新数据//delete() // 删除数据//query1() // 查询数据query2() // 查询数据
}// 数据添加
func insert() {// 单条数据添加user := Gorm{Name:   "符华",DeptId: 1,State:  1,}// Create接收的是一个指针,而不是值sql.DB.Create(&user)// 批量新增,新增10条数据var gormList []Gormfor i := 0; i < 10; i++ {email := "jiqiren" + fmt.Sprintf("%d", i) + "@qq.com"gormList = append(gormList, Gorm{Name:   fmt.Sprintf("机器人%d号", i+1),DeptId: 3,State:  2,Email:  &email, // 指针类型是为了更好的存null类型,但是传值的时候,也记得传指针})}sql.DB.Create(&gormList)
}// 数据更新
func update() {//var user Gorm//email := "fuhua@163.com"//user.Email = &email//更新指定字段//sql.DB.Model(&Gorm{}).Where("name", "符华").Update("name", "云墨")//sql.DB.Model(&Gorm{}).Where(1).Updates(Gorm{Name: "符华", Email: &email}) // 是结构体,默认不会更新零值//sql.DB.Model(&Gorm{}).Where("name = ?", "机器人1号").Updates(Gorm{DeptId: 4, Email: &email, State: 1})// Save()会更新全部字段,即使字段值是零值也会更新//sql.DB.Save(&user)                        // 更新全部字段//sql.DB.Select("email").Save(&user) // 如果不想更新所有字段,可以通过Select指定要更新的字段// Omit() 可以忽略字段
}// 数据删除
func deletes() {//var user Gorm//根据主键ID删除//sql.DB.Delete(&user, 11) // DELETE FROM `sys_gorm` WHERE `sys_gorm`.`id` = 1//sql.DB.Delete(&Gorm{}, []int{1,2,3})// 根据其他条件删除//sql.DB.Where("name like ?", "%李四%").Delete(Gorm{}) // DELETE FROM `sys_gorm` WHERE name like '%李四%'//sql.DB.Debug().Delete(Gorm{}, "name like ?", "%李四%") // DELETE FROM `sys_gorm` WHERE name like '%李四%'// 如果模型有DeletedAt字段,将自动获得软删除功能! 在调用Delete时不会从数据库中永久删除,而是只将字段DeletedAt的值设置为当前时间。软删除的记录将在查询时被忽略。//使用Unscoped查找软删除的记录//sql.DB.Unscoped().Where("name=?", "张三").Find(&user)// 使用Unscoped永久删除记录//sql.DB.Unscoped().Delete(&user)// 删除查询到的切片列表//var gormList []Gorm//sql.DB.Delete(&gormList)
}// 数据查询(单表)
func query1() {// 查询单条数据//var user Gorm//var test Test//sql.DB.Take(&user) // SELECT * FROM `sys_gorm` LIMIT 1//fmt.Println(user)//sql.DB.First(&user) // SELECT * FROM `sys_gorm` ORDER BY `id` LIMIT 1//fmt.Println(user)//sql.DB.Last(&user) // SELECT * FROM `sys_gorm` ORDER BY `id` DESC LIMIT 1//fmt.Println(user)// 根据主键查询,Take的第二个参数,默认会根据主键查询,可以是字符串,可以是数字//sql.DB.Take(&user, 2)//fmt.Println(user)//user = Gorm{} // 重新赋值//sql.DB.Take(&user, "4")//fmt.Println(user)// 根据其他条件查询//sql.DB.Take(&user, "name = ?", "机器人7号") // 使用 ? 号作为占位符,将查询内容放入 ?//sql.DB.Where(&Gorm{Name: "机器人10号"}).First(&user)//fmt.Println(user)// 根据struct查询//user.ID = 2//user.Name = "机器人1号"//sql.DB.Take(&user)//data, _ := json.Marshal(user) // 由于email是指针类型,所以看不到实际的内容,但是序列化之后,会转换为我们可以看得懂的方式//fmt.Println(string(data))//fmt.Println(user)// 通过 Scan 方法,可以将查询的结果存入另一个结构体中//sql.DB.Select("name", "dept_id").Find(&user).Scan(&test) // 这种方式会查询两次,不推荐使用//sql.DB.Model(&Gorm{}).Select("name", "dept_id").Scan(&test) // 这种方式就只查询一次了//sql.DB.Table("sys_gorm").Select("name", "dept_id").Scan(&test) // 这种方式也是只查询一次//fmt.Println(test)// 根据map查询//sql.DB.Where(map[string]any{"name": "符华", "dept_id": 1}).Find(&user)//fmt.Println(user)// 查询多条//var gormList []Gorm//var testList []Test//var count int64 // 获取查询结果的总数量//sql.DB.Find(&gormList)//for _, u := range gormList {//	data, _ := json.Marshal(u) // 序列化//	fmt.Println(string(data))//}// 根据多个主键查询//sql.DB.Not([]int64{4, 5, 6}).First(&gormList) // not in 主键//sql.DB.Find(&gormList, []int{1, 3, 5, 7})//sql.DB.Find(&gormList, []string{"1", "3", "5", "7"})//sql.DB.Find(&gormList, 1, 3, 5, 7) // 一样的//sql.DB.Find(&gormList, "1", "3", "5", "7")//fmt.Println(gormList)// 根据多个其他条件查询//sql.DB.Find(&gormList, "name in ?", []string{"符华", "机器人10号"})//sql.DB.Where("name in (?) ", []string{"机器人1号", "机器人9号"}).Find(&gormList) // in//sql.DB.Where("name<>?", "机器人10号").Find(&gormList) // 不等于//sql.DB.Where("name like ?", "%机器人%").Find(&gormList) // like//sql.DB.Where("name =? and email=?", "机器人10号", "jiqiren@qq.com").Find(&gormList) // and//sql.DB.Where("name =?", "机器人10号").Where("email=? ", "jiqiren@qq.com").Find(&gormList) // and//sql.DB.Where("name=?", "符华").Or("email=?", "jiqiren1@qq.com").Find(&gormList)         // or//sql.DB.Where(Gorm{Name: "符华"}).Or(Gorm{DeptId: 1}).Find(&gormList) // or//sql.DB.Not("name", "符华").Find(&gormList)                                              // not条件查询//sql.DB.Not("name", []string{"机器人1号", "机器人9号"}).Find(&gormList).Count(&count)       // not in//sql.DB.Debug().Order("id desc").Find(&gormList).Count(&count) // order by排序//fmt.Println("查询的总数量:", count)//fmt.Println("查询的结果集:", gormList)// 查询指定的列(使用Select会查询两次,分别用于获取指定的字段和获取完整的模型数据。)//sql.DB.Select("name", "email").Find(&gormList, "name in ?", []string{"符华", "机器人10号"}).Count(&count)//sql.DB.Select([]string{"name", "email"}).Find(&gormList) // 一样的效果//fmt.Println(count, gormList)// 使用原生sql查询//sql.DB.Raw("SELECT id, name FROM sys_gorm WHERE id = ?", 1).Find(&gormList)//fmt.Println(gormList)// 去重//var emailList []string//sql.DB.Table("sys_gorm").Select("email").Distinct("email").Scan(&emailList)//sql.DB.Table("sys_gorm").Select("distinct email").Scan(&emailList)//fmt.Println(emailList)// 分组查询//var idList []string//sql.DB.Table("sys_gorm").Select("count(id)").Group("dept_id").Scan(&idList) // 根据部门id进行分组//fmt.Println(idList)//var groupList []Group//sql.DB.Table("sys_gorm").Select("count(id)", "dept_id", "group_concat(name)").Group("dept_id").Scan(&groupList) // 根据部门名称进行分组//fmt.Println(groupList)// 查询结果查询到map中//var res []map[string]any//sql.DB.Table("sys_gorm").Find(&res)//fmt.Println(res)// 分页//pageSize := 5                      // 每次查询条数//pageNum := 2                       // 当前页//offset := (pageNum - 1) * pageSize // 计算跳过的记录数//sql.DB.Offset(offset).Limit(pageSize).Find(&gormList) // 分页查询,根据offset和limit来查询//sql.DB.Offset(offset).Limit(pageSize).Find(&gormList).Scan(&testList) // 通过Scan方法,将查询的结果集存入另一个list//sql.DB.Model(&Gorm{}).Count(&count)                                   // 获取总数量(这个数量不是每一页的数量)//fmt.Println("查询的总数量:", count)//fmt.Println("查询的结果集gormList:", gormList)//fmt.Println("查询的结果集testList:", testList)// 子查询//var gromList []Gorm// SELECT * FROM `sys_gorm` WHERE id in (SELECT `id` FROM `sys_gorm` WHERE `state` = 2)//sql.DB.Model(Gorm{}).Where("id in (?)", sql.DB.Model(Gorm{}).Select("id").Where("state", 2)).Find(&gromList)
}// 数据查询(多表关联)
func query2() {var gormList []Gorm// 关联查询部门//sql.DB.Preload("Dept").Find(&gormList)//for _, u := range gormList {//	fmt.Printf("用户 %v 所在部门是 %v\n", u.Name, u.Dept.Name)//}// 关联查询角色//var user Gorm//sql.DB.Preload("Roles").First(&user)//for _, role := range user.Roles {//	fmt.Println(user.Name, "的角色是", role.Name)//}// 多个关联sql.DB.Preload("Dept").Preload("Roles").Find(&gormList)for _, u := range gormList {var role stringfor i, r := range u.Roles {if i > 0 {role += ","}role += r.Name}fmt.Printf("用户 %v 所在部门是 %v,她的角色是 [%v]\n", u.Name, u.Dept.Name, role)}
}

ok,以上就是本篇笔记的全部内容啦~如果你觉得有用,欢迎点个大拇指😘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/104929.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

接口经典题目

​ White graces&#xff1a;个人主页 &#x1f649;专栏推荐:《Java入门知识》&#x1f649; &#x1f649; 内容推荐:继承与组合&#xff1a;代码复用的两种策略&#x1f649; &#x1f439;今日诗词:人似秋鸿来有信&#xff0c;事如春梦了无痕。&#x1f439; 目录 &…

剪枝基础与实战(3): 模型剪枝和稀疏化训练流程

Model Pruning 相关论文:Learning Efficient Convolutional Networks through Network Slimming (ICCV 2017) 考虑一个问题,深度学习模型里面的卷积层出来之后的特征有非常多,这里面会不会存在一些没有价值的特征及其相关的连接?又如何去判断一个特征及其连接是否有价值? …

Packet_Tracer的使用

一、实验目的&#xff1a; 通过该实验了解Packet Tracer的使用方法&#xff0c;能够用Packet Tracer建立和模拟网络模型。 二、主要任务&#xff1a; 1.熟悉PT的界面&#xff0c;了解按键用途。 2.尝试自己建立一个小型网络&#xff0c;并测试连通性。 3.学习P…

5.6.webrtc三大线程

那今天呢&#xff1f;我们来介绍一下web rtc的三大线程&#xff0c;那为什么要介绍这三大线程呢&#xff1f;最关键的原因在于web rtc的所有其他线程都是由这三大线程所创建的。那当我们将这三个线程理解清楚之后呢&#xff1f;我们就知道其他线程与它们之间是怎样关系&#xf…

stm32 之20.HC-06蓝牙模块

原理图显示使用usart3串口使用的是PB10和PB11引脚 直接配置usart3串口协议 void usart3_init(uint32_t baud) {GPIO_InitTypeDef GPIO_InitStructureure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;//端口B硬件时钟打开RCC_AHB1PeriphClockC…

mysql my.ini、登录、用户相关操作、密码管理、权限管理、权限表

my.ini 配置文件格式 登录mysql mysql -h hostname | IP -P port -u username -p database -e “select 语句”&#xff1b; 创建用户、修改用户、删除用户 create user ‘zen’ identified by ‘密码’ ## host 默认是 % create user ‘zen’‘localhost’ identified by ‘密…

Stable Diffusion 系列教程 | 如何获得更高清优质的AI绘画

目录 1 高清修复 1.1 原理 1.2 基本操作 1.3 优缺点 2 UpScale 放大脚本 2.1 原理 2.2 基本操作 2.3 优缺点 3 附加功能放大 3.1 原理 3.2 基本操作 3.3 优缺点 优化出图质量&#xff0c;产出更高清&#xff0c;分辨率更高&#xff0c;更有细节的绘画作品呢&#x…

禾赛科技第二季度财报超出预期,但全年业绩指引却令投资者失望

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 华尔街分析师对禾赛科技2023年第二季度的财报预测 在禾赛科技&#xff08;HSAI&#xff09;公布第二季度财报之前&#xff0c;华尔街分析师和公司管理层就预计禾赛科技在2023年第二季度的财务业绩将会很不错。 在收入增长方…

工控机引领移动机器人的智能化革命!

随着制造业数字化转型的加速&#xff0c;工业4.0时代的到来&#xff0c;工业互联网逐步成为中国推进新型工业化进程的核心驱动力量。而工控机作为工业互联网领域的重要组成部分&#xff0c;已经在越来越多行业得到广泛应用。据中商情报网数据显示&#xff0c;2022年我国工业自动…

【微服务】03-HttpClientFactory与gRpc

文章目录 1.HttpClientFactory &#xff1a;管理外向请求的最佳实践1.1 核心能力1.2 核心对象1.3 HttpClient创建模式 2.gRPC&#xff1a;内部服务间通讯利器2.1 什么是gRPC2.2 特点gRPC特点2.3.NET生态对gRPC的支持情况2.4 服务端核心包2.5 客户端核心包2.5 .proto文件2.6 gRP…

excel 分组排序

excel中会遇到对不同分组数据进行排序&#xff0c;比如对于不同班级里的学生按照分数高低进行升序排序&#xff0c;可以采用如下公式 SUMPRODUCT((A$2:A$12A2)*(C$2:C$12>C2))1 如果需要 进行降序排序&#xff0c;将公式中的大于号替换为小于号即可

优秀产品奖!移远5G RedCap模组,让5G真正“轻”下来

8月24日&#xff0c;在通信世界全媒体主办的“5G RedCap技术与物联网应用创新研讨会”上&#xff0c;“5G RedCap优秀产品和解决方案”获奖名单发布&#xff0c;移远通信5G RedCap模组Rx255C系列以其在创新性、实用性、经济性、成熟性等方面的综合领先优势&#xff0c;获此殊荣…

Android studio 2022.3.1 鼠标移动时不显示快速文档

在使用技术工具的过程中&#xff0c;我们时常会遇到各种各样的问题和挑战。最近&#xff0c;我升级了我的Android Studio到2022.3.1版本&#xff0c;但是在使用过程中&#xff0c;我碰到了一个让我颇为困扰的问题&#xff1a;在鼠标移动到类名或字段上时&#xff0c;原本应该显…

长胜证券:货币政策什么意思?

钱银政策是指国家钱银当局经过调控钱银供给量和利率等手法&#xff0c;以到达操控通货膨胀、坚持经济稳定、促进经济增长等目的的一种宏观经济政策。简而言之&#xff0c;钱银政策便是国家中央银行对钱银供给和利率进行调控的政策。那么具体来说&#xff0c;钱银政策到底有哪些…

动态规划入门:斐波那契数列模型以及多状态(C++)

斐波那契数列模型以及多状态 动态规划简述斐波那契数列模型1.第 N 个泰波那契数&#xff08;简单&#xff09;2.三步问题&#xff08;简单&#xff09;3.使⽤最⼩花费爬楼梯&#xff08;简单&#xff09;4.解码方法&#xff08;中等&#xff09; 简单多状态1.打家劫舍&#xff…

Docker常见配置实验

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 1、拉取mysql5.6与owncloud的镜像 docker pull mysql:5.6 docker pull mysql:5.6 2、生成容器实例&#xff0c;构建个人网盘 docker run -d --name mydb1 --env MYSQL_ROOT_PASSWORD123456 mysql:5.6 docker …

【学习FreeRTOS】第17章——FreeRTOS任务通知

1.任务通知的简介 任务通知&#xff1a;用来通知任务的&#xff0c;任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。 使用队列、信号量、事件标志组时都需另外创建一个结构体&#xff0c;通过中间的结构体进行间接通信&#xff01; 使用任务通知时&#xff0c…

element-table的动态操作,自动以表格,动态新增行、列,删除行列

灵活的自定义表格行列以及增删改查的操作,右键选中列则是列的删除&#xff0c;效果如下 <template><div class"st-table"><div style"width: 100%"><el-button click"addRow()" type"primary" icon"CircleP…

在CSS中,盒模型中的padding、border、margin是什么意思?

在CSS中&#xff0c;盒模型&#xff08;Box Model&#xff09;是用来描述和布局HTML元素的基本概念。它将每个HTML元素看作是一个矩形的盒子&#xff0c;这个盒子包括了内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&a…

无涯教程-PHP - intval() 函数

PHP 7引入了一个新函数 intdiv()&#xff0c;该函数对其操作数执行整数除法并将该除法返回为int。 <?php$valueintdiv(10,3);var_dump($value);print(" ");print($value); ?> 它产生以下浏览器输出- int(3) 3 PHP - intval() 函数 - 无涯教程网无涯教程网…