【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.mzph.cn/news/51106.shtml

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

相关文章

【ag-grid-vue】基本使用

ag-grid是一款功能和性能强大外观漂亮的表格插件&#xff0c;ag-grid几乎能满足你对数据表格所有需求。固定列、拖动列大小和位置、多表头、自定义排序等等各种常用又必不可少功能。关于收费的问题&#xff0c;绝大部分应用用免费的社区版就够了&#xff0c;ag-grid-community社…

MATLAB打开excel读取写入操作例程

本文使用素材含代码测试用例等 MATLAB读写excel文件历程含&#xff0c;内含有测试代码资源-CSDN文库 打开文件 使用uigetfile函数过滤非xlsx文件&#xff0c;找到需要读取的文件&#xff0c;首先判断文件是否存在&#xff0c;如果文件不存在&#xff0c;程序直接返回&#x…

线上问诊:业务数据采集

系列文章目录 线上问诊&#xff1a;业务数据采集 文章目录 系列文章目录前言一、环境准备1.Hadoop2.Zookeeper3.Kafka4.Flume5.Mysql6.Maxwell 二、业务数据采集1.数据模拟2.采集通道 总结 前言 暑假躺了两个月&#xff0c;也没咋写博客&#xff0c;准备在开学前再做个项目找…

elementui表格嵌套上传文件直传到oss服务器(表单上传)

提示&#xff1a;记录项目中遇到的问题&#xff0c;仅供参考 文章目录 前言一、vue代码二、js接口请求代码 前言 项目需求是在表格中嵌套一个上传图片的功能&#xff0c;并且回显选择的图片和已上传的图片&#xff0c;再通过点击操作列中上传按钮才开始上传&#xff0c;使用的…

如遭遇DDoS等攻击会对企业和个人造成严重影响,包括以下

1. 服务不可用&#xff1a;正常用户无法访问目标服务器&#xff0c;导致业务中断&#xff0c;影响用户体验。 2. 数据泄露&#xff1a;攻击者可能会在攻击过程中窃取用户数据&#xff0c;导致隐私泄露和财产损失。 3. 经济损失&#xff1a;由于服务中断&#xff0c;企业可能遭受…

C语言刷题训练DAY.12

1.统计成绩 解题思路&#xff1a; 这里我们设置两个变量记录最大值和最小值&#xff0c;再用一个sum统计分数总和即可。 解题代码&#xff1a; #include<stdio.h> int main() {int n 0;scanf("%d", &n);double arr[100] { 0 };int i 0;//最高分double …

postgresql 数据排序

postgresql 常见操作 排序总结 排序 -- 排序的时候null是最大的值(看一下) select employee_id,manager_id from employeesorder by manager_id desc;-- nulls first使null值排在第一位 select employee_id,manager_id from employeesorder by manager_id nulls first;-- null…

财务数据分析用什么软件好?财务数据分析的几个重要数据是什么?

财务的数据分析也分很多种的&#xff0c;就拿最粗略的划分来说&#xff0c;也可以分为3大领域—— 财务数据处理类工具财务数据挖掘类工具财务数据可视化工具 01 数据处理类 在财务数据处理这一块儿&#xff0c;不用说&#xff0c;当然是以excel为主力的数据处理类工具—— …

Protobuf在IDEA中的插件安装教程

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

海思Hi3861L开发三-新建自定义项目

一、简介 上一篇文章,介绍了如何下载安装SDK,并且编译和下载。但都是基于SDK原生提供的demo。那本节我们就来介绍一下,如果创建一个自己的项目。 二、新建目录结构 先看SDK提供的目录结构,如下图: 因为是基于应用的开发,所以我们只关注app这个文件夹。可以看到…

纠缠辅助的量子网络:原理、技术、发展与挑战

7月11日&#xff0c;中国科大网络空间安全学院和陆军院士工作室李忠辉博士为第一作者、薛开平教授为通讯作者的量子网络综述论文“Entanglement-Assisted Quantum Networks: Mechanics, Enabling Technologies, Challenges, and Research Directions”在通信领域知名期刊《IEEE…

Ext JS 之Microloader(微加载器)

“Microloader”是 Sencha 数据驱动的 JavaScript 和 CSS 动态加载器的名称。 清单 app.json 用于应用的设置,Sencha Cmd 在构建的时候会读取这个文件。 Sencha Cmd 转换“app.json”的内容并将生成的清单传递给 Microloader 以在运行时使用。 最后,Ext JS 本身也会查阅运…

c++学习之内存管理

目录 1.c/c内存分布 2.new与delete/malloc与free c内存管理方式&#xff1a; new/delete操作内置类型&#xff1a; new/delete操作自定义类型 operator new与operator delete函数 new和delete的实现原理 内置类型 自定义类型 malloc/free和new/delete的区别 1.c/c内存分…

iPhone 14 Pro 动态岛的功能和使用方法详解

当iPhone 14 Pro机型发布时,苹果公司将软件功能与屏幕顶部的药丸状切口创新集成,称之为“灵动岛”,这让许多人感到惊讶。这篇文章解释了它的功能、工作原理,以及你如何与它互动以执行动作。 一、什么是灵动岛?它是如何工作的 在谣言周期的早期‌iPhone 14 Pro‌ 在宣布时…

js 模块 简单实验

1.代码 1.1 t2.js var year "test"; export { year }; 1.2 t1.js import { year } from ./t2.jsalert(year); 1.3 t.html <script type"module" src"./t1.js"></script> 2.运行结果

边缘计算节点BEC典型实践:如何快速上手PC-Farm服务器?

百度智能云边缘计算节点BEC&#xff08;Baidu Edge Computing&#xff09;基于运营商边缘节点和网络构建&#xff0c;一站式提供靠近终端用户的弹性计算资源。边缘计算节点在海外覆盖五大洲&#xff0c;在国内覆盖全国七大区、三大运营商。BEC通过就近计算和处理&#xff0c;大…

rust actix-web定义中间件(middleware)记录接口耗时(接口耗时中间件和鉴权中间件)

文章目录 Actix-web定义中间件(middleware)记录接口耗时中间件简介中间件添加的两种方式&#xff08;接口耗时中间件&#xff09;使用wrap_fn 闭包实现使用warp struct实现中间件调用顺序actix自带的接口耗时中间件 鉴权中间件 Actix-web定义中间件(middleware)记录接口耗时 …

性能测试工具Jmeter你所不知道的内幕

谈到性能测试&#xff0c;大家一定会联想到Jmeter和LoadRunner,这两款工具目前在国内使用的相当广泛&#xff0c;主要原因是Jmeter是开源免费&#xff0c;LoadRunner 11在现网中存在破解版本。商用型性能测试工具对于中小型企业很难承担相关的费用。国内的性能测试工具有&#…

solidity0.8.0的应用案例11:透明代理合约

选择器冲突 智能合约中,函数选择器(selector)是函数签名的哈希的前4个字节。例如mint(address account)的选择器为bytes4(keccak256("mint(address)")),也就是0x6a627842. 由于函数选择器仅有4个字节,范围很小,因此两个不同的函数可能会有相同的选择器,例如…

使用Nodejs创建简单的HTTP服务器,借助内网穿透工具实现公网访问的方法分享

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…