【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.hqwc.cn/news/86613.html

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

相关文章

【应用层】网络基础 -- HTTPS协议

HTTPS 协议原理加密为什么要加密常见的加密方式对称加密非对称加密 数据摘要&&数据指纹 HTTPS 的工作过程探究方案1-只使用对称加密方案2-只使用非对称加密方案3-双方都使用非对称加密方案4-非对称加密对称加密中间人攻击-针对上面的场景 CA认证理解数据签名方案5-非对…

《golang设计模式》第二部分·结构型模式-03-组合模式(Composite)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 将叶子节点和复合节点组合起来&#xff0c;定义一个抽象接口遍历他们 1.1 角色 Component&#xff08;抽象构件&#xff09;&#xff1a;为叶子构件和复合构件声明接口&#xff0c;定义了结构…

git及GitHub的使用

文章目录 git在本地仓库的使用github使用创建仓库https协议连接(不推荐&#xff0c;现在用起来比较麻烦)ssh连接&#xff08;推荐&#xff09;git分支操作冲突处理忽略文件 git在本地仓库的使用 1.在目标目录下右键打开git bash here 2.创建用户名和邮箱(注&#xff1a; 下载完…

GeoHash之存储篇

前言&#xff1a; 在上一篇文章GeoHash——滴滴打车如何找出方圆一千米内的乘客主要介绍了GeoHash的应用是如何的&#xff0c;本篇文章我想要带大家探索一下使用什么样的数据结构去存储这些Base32编码的经纬度能够节省内存并且提高查询的效率。 前缀树、跳表介绍&#xff1a; …

Python 包管理(pip、conda)基本使用指南

Python 包管理 概述 介绍 Python 有丰富的开源的第三方库和包&#xff0c;可以帮助完成各种任务&#xff0c;扩展 Python 的功能&#xff0c;例如 NumPy 用于科学计算&#xff0c;Pandas 用于数据处理&#xff0c;Matplotlib 用于绘图等。在开始编写 Pytlhon 程序之前&#…

ms-tpm-20-ref 在linux下编译

1、代码地址&#xff0c; GitHub - microsoft/ms-tpm-20-ref: Reference implementation of the TCG Trusted Platform Module 2.0 specification.Reference implementation of the TCG Trusted Platform Module 2.0 specification. - GitHub - microsoft/ms-tpm-20-ref: Refe…

技术的巅峰演进:深入解析算力网络的多层次技术设计

在数字化时代的浪潮中&#xff0c;网络技术正以前所未有的速度演进&#xff0c;而算力网络作为其中的一颗明星&#xff0c;以其多层次的技术设计引领着未来的网络构架。本文将带您深入探索算力网络独特的技术之旅&#xff0c;从底层协议到分布式控制&#xff0c;为您呈现这一创…

postman-使用Postman的模拟服务来模拟(mock)后端数据,完成前端模拟API调用

最近项目上比较忙&#xff0c;任务多时间紧&#xff0c;导致后端开发任务繁多&#xff0c;无法及时开发完毕&#xff0c;但是前端同学已经把对应功能开发完成&#xff0c;需要进行前后端联调来验证API及一些交互问题&#xff1b;这不能因为后端的进度来影响前端的工作完成情况&…

财务数据分析怎么做?看看奥威BI数据可视化工具的解法

从以往的BI智能数据可视化分析项目来看&#xff0c;要想快刀砍乱麻地做好财务数据分析&#xff0c;为企业运营决策提供更加直观深入的数据支持&#xff0c;那就需要为财务数据分析做好数据导入、建模、报表制作、展示等多方面的准备。奥威BI数据可视化工具为此特意打造了一套标…

窗口看门狗

从下往上看: 1. 时钟设置 RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//使能独立看门狗时钟 WWDG_SetPrescaler(WWDG_Prescaler_8);//看门狗预分频器WWDG counter clock (PCLK1/4096)/8 2.设置窗口值 实际就是设置WWDG_CR的低七位值, 但是这个值要大于0x40(也就是…

使用 MATLAB 和 Simulink 对雷达系统进行建模和仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

opencv 进阶20-随机森林示例

OpenCV中的随机森林是一种强大的机器学习算法&#xff0c;旨在解决分类和回归问题。随机森林使用多个决策树来进行预测&#xff0c;每个决策树都是由随机选择的样本和特征组成的。在分类问题中&#xff0c;随机森林通过投票来确定最终的类别&#xff1b;在回归问题中&#xff0…