go gin学习记录2

news2024/11/25 0:48:06

环境

环境:mac m1,go version 1.17.2, goland, mysql

昨天学习了安装和基本的参数,路由使用,今天接着学习一下数据库的操作。

建立数据库

测试数据库操作,需要先准备一个测试用的数据库,那我们就来建一个测试用数据库t_gin和数据表user:

mysql> create database t_gin;
Query OK, 1 row affected (0.01 sec)

mysql> use t_gin;
Database changed
mysql> create table user(
    -> id int not null auto_increment,
    -> name varchar(32) not null default "" comment "user name",
    -> birth varchar(32) not null default "" comment "user birthday",
    -> primary key(id)
    -> )engine=innodb comment "user info record";
Query OK, 0 rows affected (0.05 sec)

mysql>

好了,测试用的数据库和表已经建好了,接下来开始撸码。
先从原生SQL开始。

安装mysql驱动

要操作mysql,需要先装一下驱动:

$ go get github.com/go-sql-driver/mysql
go get: added github.com/go-sql-driver/mysql v1.7.0

驱动安装好了。

原生SQL-插入操作

想一下,基础的数据库操作包含插入insert、更新update以及使用最多的查询select操作,而我们要操作的目标数据表是user,所以在controller/目录下我们新建一个user.go文件,用来存放对user表的操作。
代码如下:

package controller

import (
	"github.com/gin-gonic/gin"
)

type UserController struct {
}

type User struct {
	ID    int
	Name  string
	Birth string
}

func (u *UserController) CreateUser(c *gin.Context) {

}

func (u *UserController) GetUserInfo(c *gin.Context) {

}

func (u *UserController) UpdateUserInfo(c *gin.Context) {

}

先把3个基础操作的处理列出来,内容一会儿再具体填充。
handle现在有了,路由我们也要来定义一下,按照昨天学的路由分组,user相关的这几个操作可以都归到一个group下,就叫user好了。

在main.go中,新增一个路由组user,用来测试插入、获取、更新操作,代码如下:

	user := r.Group("/user")
	{
		userCtrl := controller.UserController{}
		user.POST("/createUser", userCtrl.CreateUser)
		user.GET("/getUserInfo", userCtrl.GetUserInfo)
		user.POST("/updateUserInfo", userCtrl.UpdateUserInfo)
	}

好的,代码框架现在有了。
由于是做测试,所以user表中只有3个字段,id\name\age,而id又是自增的主键,那在插入、更新操作中,我们需要操作的就只有name\age两个字段。
先完善插入操作CreateUser:

package controller

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

type UserController struct {
}

type User struct {
	ID    int
	Name  string
	Birth string
}

func (u *UserController) CreateUser(c *gin.Context) {
	name := c.PostForm("name")
	birth := c.PostForm("birth")

	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/t_gin?charset=utf8&parseTime=true")
	if err != nil {
		log.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{
			"code": 1,
			"data": false,
		})
	}

	result, err := db.Exec("insert into user(name,birth) values(?,?)", name, birth)
	if err != nil {
		log.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{
			"code": 1,
			"data": false,
		})
	}

	ret := make(map[string]interface{})
	ret["id"], _ = result.LastInsertId()
	ret["effectRows"], _ = result.RowsAffected()

	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"data": ret,
	})
}

func (u *UserController) GetUserInfo(c *gin.Context) {

}

func (u *UserController) UpdateUserInfo(c *gin.Context) {

}

好的,插入操作已经完善了,那我们测试看一下
在这里插入图片描述

看这个返回是发生异常了,没有得到预期的结果,看一下终端什么情况

2023/02/14 15:51:26 sql: unknown driver "mysql" (forgotten import?)


2023/02/14 15:51:26 [Recovery] 2023/02/14 - 15:51:26 panic recovered:
POST /user/createUser HTTP/1.1
Host: localhost:8080
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 268
Content-Type: multipart/form-data; boundary=--------------------------217154931077337827823686
Postman-Token: 4b35385e-4d83-4fec-8d50-ec6779f2cd53
User-Agent: PostmanRuntime/7.30.1


runtime error: invalid memory address or nil pointer dereference

看样子是没有引入mysql的驱动,加一下

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"net/http"
)

然后我们重新启动,再测试看看
在这里插入图片描述
看返回这次是成功了,那我们看下mysql中是否已经有了数据

mysql> select * from user;
+----+-------+----------+
| id | name  | birth    |
+----+-------+----------+
|  1 | Alice | 2002-2-3 |
+----+-------+----------+
1 row in set (0.03 sec)

mysql>

好的,mysql中也有数据了,插入操作尝试成功,符合预期结果。

原生SQL-获取操作

数据有写入,就会有获取。而在日常工作中,获取数据是使用频次最高的操作,那么接下来就尝试完善查询操作。
完善后的查询操作代码如下:

func (u *UserController) GetUserInfo(c *gin.Context) {
	id := c.Query("id")
	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/t_gin?charset=utf8&parseTime=true")
	if err != nil {
		log.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{
			"code": 1,
			"data": false,
		})
	}

	row, err := db.Query("select * from user where id=?", id)
	if err != nil {
		log.Println(err.Error())
	}

	var user User
	row.Scan(&user)

	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"data": user,
	})
}

那我们跑一下看看是否符合预期
在这里插入图片描述
麻蛋,没获取到数据。
看这个样子,应该是数据没绑定到我们定义的struct上,检查一下,确实是绑定的时候没有按照scan的方式来,把这里调整一下:

	var user User
	err = row.Scan(&user.ID, &user.Birth, &user.Name)
	if err != nil {
		log.Println(err.Error())
	}

再试试看是不是解决了问题
在这里插入图片描述
看这返回应该是解决了个鸟蛋。。。
看终端的输出还有条信息打印:

2023/02/14 16:49:19 sql: Scan called without calling Next
[GIN] 2023/02/14 - 16:49:19 | 200 |     6.09775ms |             ::1 | GET      "/user/getUserInfo?id=1"

看这提示是说scan应该调用next,这单条记录查询也要用next吗?好吧先修改一下看看。
将scan部分的代码调整一下:

	var user User
	for row.Next() {
		err = row.Scan(&user.ID, &user.Birth, &user.Name)
		if err != nil {
			log.Println(err.Error())
		}
	}

来吧,见证奇迹的时刻到了!
在这里插入图片描述
这下子数据是有了,但是这个数据不太对,字段和值混乱了。
难道scan的时候必须按照表的字段顺序来?
试试看
继续调整scan代码

	var user User
	for row.Next() {
		err = row.Scan(&user.ID, &user.Name, &user.Birth)
		if err != nil {
			log.Println(err.Error())
		}
	}

在这里插入图片描述
这次结果正常了,终于符合我们的预期了。

还是对使用scan.next有些疑惑,又翻了翻文档,发现有一个QueryRow方法,我们调整一下代码,看看是不是符合:

	row := db.QueryRow("select * from user where id=?", id)

	var user User
	err = row.Scan(&user.ID, &user.Name, &user.Birth)
	if err != nil {
		log.Panic(err.Error())
	}

运行之后发现是符合预期的。

QueryRow是查询单行数据,获取到的数据是一条,可能直接scan进行操作;
而Query获取到的是数据集,数据集中可能包含0条或多条数据,所以使用Query方法查询出来的数据,就得使用next方法去迭代出来。
那么就好理解了,获取全量数据的场景“select * from user"就必须使用Query来处理了,获取单挑记录可以走QueryRow。

原生SQL-更新操作

接下来就是更新了。
先完善一下更新操作的代码,完善之后是这样:

func (u *UserController) UpdateUserInfo(c *gin.Context) {
	id := c.PostForm("id")
	name := c.PostForm("name")
	birth := c.PostForm("birth")

	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/t_gin?charset=utf8&parseTime=true")
	if err != nil {
		log.Println(err.Error())
		c.JSON(http.StatusOK, gin.H{
			"code": 1,
			"data": false,
		})
	}

	result, err := db.Exec("update user set name=?,birth=? where id=?", name, birth, id)
	if err != nil {
		log.Println(err.Error())
	}
	
	c.JSON(http.StatusOK, gin.H{
		"code":0,
		"data":result.RowsAffected(),
	})
}

启动一下进行测试,发现报了个错误

$ go run main.go
# t_gin/controller
controller/user.go:110:30: multiple-value result.RowsAffected() in single-value context

看提示是RowsAffected()返回两个值,不匹配,我们调整一下

	eff, _ := result.RowsAffected()
	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"data": eff,
	})

然后再运行测试看一看
在这里插入图片描述
好的,看返回是正常的,那我们看看mysql中的数据是不是确实被更新了

mysql> select * from user;
+----+------+----------+
| id | name | birth    |
+----+------+----------+
|  1 | lucy | 2002-5-1 |
+----+------+----------+
1 row in set (0.00 sec)

mysql>

确实被更新了,数据没问题。
这样,更新操作也测试通过了。
至此,插入、获取、更新这3个最基本的数据库操作,我们在gin框架中使用go的原生sql操作都完成了实现。

今天就到这里。

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

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

相关文章

【自适应软件开发过程思想】自适应软件开发 - 简介 Adaptive Software Development Introduction(中英文)

目录 ASWD 核心思想 什么是敏捷(Agile)? 敏捷宣言(Agile Manifesto) 敏捷的特征

微前端指北

微前端概述 Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. – Micro Frontends 什么是微前端 微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 …

GAN和CycleGAN

文章目录1. GAN 《Generative Adversarial Nets》1.1 相关概念1.2 公式理解1.3 图片理解1.4 熵、交叉熵、KL散度、JS散度1.5 其他相关(正在补充!)2. Cycle GAN 《Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Ne…

Spring事务Spring整合MyBatis

Spring整合MyBatis 在整合之前需要保证mybatis、springframework、mybatis-spring、spring-jdbc的包都已经导入 并且要根据官网提示保证mybatis、springframework、mybatis-spring版本相对应&#xff0c;否则会产生冲突报错 maven依赖如下&#xff1a; <!-- Spring Web…

SAS应用入门学习笔记6

SQL (SAS): Features&#xff1a; 1&#xff09;不需要在每个query中重复调用每个SQL&#xff1b; 2&#xff09;每个statement都是独立去完成的&#xff1b; 3&#xff09;我们是没有proc print和proc sort语句的&#xff1b;&#xff08;order by&#xff09; key synta…

vue3的shallowReactive和shallowRef,readonly和shallowReadonly

shallowReactive&#xff1a;只处理对象最外层属性的响应式&#xff08;浅响应式&#xff09; <template><span>姓名&#xff1a;{{name}}</span><span>年龄&#xff1a;{{age}}</span><span>职业&#xff1a;{{salary.job.num}}</spa…

JAVA SE复习(第5章 面向对象(上))

本文笔记来自硅谷柴林燕老师的笔记 只为自己看笔记方便使用 不做他用5.1 面向对象编程5.1.2 类和对象1、什么是类类是一类具有相同特性的事物的抽象描述&#xff0c;是一组相关属性和行为的集合。属性&#xff1a;就是该事物的状态信息。行为&#xff1a;就是在你这个程序中&am…

springboot集成kafka

选择特定版本spring-kafka官方使用文档 进入官网&#xff1a;https://spring.io/ 选择自己使用版本 点进去后按照 /2.7.0/reference/html/ 路径点进去就能看到想要版本的文档了

C++实现线程池

C实现线程池一、前言二、线程池的接口设计2.1、类封装2.2、线程池的初始化2.3、线程池的启动2.4、线程池的停止2.5、线程的执行函数run()2.6、任务的运行函数2.7、等待所有线程结束三、测试线程池四、源码地址总结一、前言 C实现的线程池&#xff0c;可能涉及以下知识点&#…

JVM - 垃圾回收

目录 垃圾回收基础和根搜索算法 垃圾回收概述 根搜索算法 引用分类 垃圾回收基础(跨代引用、记忆集、写屏障、判断垃圾的步骤、STW) 跨代引用 记忆集(Remembered Set) 写屏障 判断是否垃圾的步骤 GC类型 Stop-The-World 垃圾收集类型 判断类无用的条件 垃圾回收算…

Mybatis源码(2) - SqlSessionTemplate的介绍及创建过程

0. 前言1. Spring对SqlSessionTemplate的管理1.1. SqlSessionTemplate的创建&#xff1a;1.2. MapperProxy中sqlSession的来源&#xff1a;2. SqlSessionInterceptor中的getSqlSession0. 前言 众所周知&#x1f60f;:MyBatis通过SqlSessionFactory 创建SqlSession去调用Executo…

在VMware17 Pro中设置创建虚拟机Ubuntu 20

在VMware17 Pro中设置创建虚拟机Ubuntu 200 前言1 安装Ubuntu 20步骤0 前言 书接上回&#xff0c;安装好了VMware17 Pro之后&#xff0c;就是安装虚拟机了&#xff0c;前提是下好了系统安装包&#xff0c;以Ubuntu 20为例 1 安装Ubuntu 20步骤 首先点击创建新的虚拟机 新建…

利用NAS免费部署动态解析实现内网穿透

‍ 想要从外网访问家中的NAS等设备&#xff0c;一般来说我们需要知道家中路由器的公网IP。 现在固定的公网IP基本上很难免费申请到了&#xff0c;但是一般来说运营商可以免费提供一个动态变化的公网IP&#xff1a;当路由设备重启时&#xff0c;运营商会给你重新分配一个新的I…

PHP加载3D模型【WebGL】

这是另一篇关于如何使用 PHP加载 3D 模型的文章。 在这里&#xff0c;我使用 Laravel 作为后端及其存储。 我在前端使用 Three.Js 库来渲染和显示 3D 模型。 我将向您展示如何上传 3D 模型以及如何从 Laravel 存储加载 3D 模型。 请仔细完成以下步骤。 大家可以在评论区提出任何…

8Manage PPM项目管理系统独特的功能:项目完整性保护

项目有其内在复杂性&#xff08;项目管理的科学部分&#xff09;&#xff0c;这种复杂性可以进行划分和克服。项目也有人为的或偶然的复杂性&#xff08;项目管理的艺术部分&#xff09;&#xff0c;这种复杂性无法进行划分和克服。偶然的高复杂性会影响并使内在复杂性难以管理…

【系统架构设计师】计算机组成与体系结构 ① ( 计算机组成 | CPU | 存储器 | 总线 | IO 外设 | CPU 组成 | 运算器 | 控制器 )

文章目录一、计算机组成与体系结构二、计算机组成结构三、CPU 组成1、运算器2、控制器一、计算机组成与体系结构 计算机组成与体系结构 对应 大学的 计算机组成原理 课程 , 主要分为 : 计算机组成体系结构划分 两大知识板块 ; 在架构师考试时 , 平均分值是 3 分 ; 计算机组成…

三相可控全桥整流与DC Buck变换电路设计仿真问题汇总

目 录 问题 一、开关管没有打开的情况下&#xff0c;DC Buck输出负电压&#xff1f; 二、问题分析 1.输出端存在与母线电压反相的电压&#xff0c;因此可以确定为差模感应电压&#xff0c;如果输出端与母线端产生的是大小相等&#xff0c;方向相同的同相电压&#xff0c;则为共…

大数据框架之Hadoop:HDFS(六)DataNode(面试开发重点)

6.1DataNode工作机制 DataNode工作机制&#xff0c;如下图所示。 1&#xff09;一个数据块在DataNode上以文件形式存储在磁盘上&#xff0c;包括两个文件&#xff0c;一个是数据本身&#xff0c;一个是元数据包括数据块的长度&#xff0c;块数据的校验和&#xff0c;以及时间戳…

ROS笔记(4)——发布者Publisher与订阅者Subscribe的编程实现

发布者 以小海龟的话题消息为例,编程实现发布者通过/turtle1/cmd_vel 话题向 turtlesim节点发送消息&#xff0c;流程如图 步骤一 创建功能包&#xff08;工作空间为~/catkin_ws/src&#xff09; $ cd ~/catkin_ws/src $ catkin_create_pkg learning_topic roscpp rospy s…

FLStudio水果最新版本V21支持中文语言

FL Studio简称FL&#xff0c;全称&#xff1a;Fruity Loops Studio习惯叫它水果。软件现有版本是FLStudio21&#xff0c;已全面升级支持简体中文语言界面 。FL Studio 能让你的计算机就像是全功能的录音室一样&#xff0c;完成编曲、剪辑、录音、混音等工作&#xff0c;帮助爱好…