天天看點

go gin學習記錄3

環境

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

安裝gorm

第二節學習了在gin中使用go的原生SQL進行操作,這節學習一下使用orm。

go的orm包有很多,gorm是使用較多較廣的,是以我們就用gorm來學習。

首先要安裝gorm以及相關的驅動:

$ go get -u gorm.io/gorm
go get: added github.com/jinzhu/inflection v1.0.0
go get: added github.com/jinzhu/now v1.1.5
go get: added gorm.io/gorm v1.24.5
$ go get -u gorm.io/driver/mysql
go get: added gorm.io/driver/mysql v1.4.6
           

安裝好gorm之後,我們準備一下要使用的測試資料表,就叫student好了。

建測試表

mysql> create table student(
    -> id int not null auto_increment,
    -> name varchar(32) not null default "" comment "student name",
    -> birth varchar(32) not null default "" comment "student birth",
    -> primary key(id)
    -> )engine=innodb comment "student info";
           

好的,需要準備的東西都準備好了,下面開始撸碼。

搭建代碼架子

類似第二節,我們将對student的具體操作都放在一處,還是在controller/目錄下建立student.go檔案。

package controller

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

type StudentController struct {
}

type Student struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Birth string `json:"birth"`
}

func (s *StudentController) CreateStudent(c *gin.Context) {

}

func (s *StudentController) GetStudentInfo(c *gin.Context) {

}

func (s *StudentController) UpdateStudent(c *gin.Context) {

}
           

然後在入口main.go中增加一個對student操作的路由組:

student := r.Group("/student")
	{
		stuCtrl := controller.StudentController{}
		student.POST("/createStudent", stuCtrl.CreateStudent)
		student.GET("/getStudentInfo", stuCtrl.GetStudentInfo)
		student.POST("/updateStudent", stuCtrl.UpdateStudent)
	}
           

完善插入操作

下面開始完善具體實作。

由于多個方法中都需要使用dsn,是以先把dsn作為一個全局變量放外邊定義

完善代碼,從插入功能開始。

func (s *StudentController) CreateStudent(c *gin.Context) {
	var stu Student
	stu.Name = c.PostForm("name")
	stu.Birth = c.PostForm("birth")

	db, err := gorm.Open(mysql.Open(dsn))
	if err != nil {
		log.Panic(err.Error())
	}

	result := db.Create(stu)
	if result.Error != nil {
		log.Panic(result.Error.Error())
	}

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

好的,啟動項目,調用一下看看結果

go gin學習記錄3

出錯了。

看一下終端的資訊:

Error 1146 (42S02): Table 't_gin.students' doesn't exist
           

這意思是說t_gin資料庫裡沒有找到students表。

這當然找不到,我們建的表明是student。。。是以這是把我們傳入的student參數當成要操作的表名了?後面又給加了個s是以成了students?

為了驗證這個猜想,我們換個struct當參數試一下

type Haha struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Birth string `json:"birth"`
}
           

複制一份student,就稱之為Haha吧。然後把插入操作的代碼調整一下

func (s *StudentController) CreateStudent(c *gin.Context) {
	/*var stu Student
	stu.Name = c.PostForm("name")
	stu.Birth = c.PostForm("birth")*/
	var haha Haha
	haha.Name = c.PostForm("name")
	haha.Birth = c.PostForm("birth")

	db, err := gorm.Open(mysql.Open(dsn))
	if err != nil {
		log.Panic(err.Error())
	}

	result := db.Create(haha)
	if result.Error != nil {
		log.Panic(result.Error.Error())
	}

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

我們用修改後的代碼再重新運作一下,調用看看結果如何:

Error 1146 (42S02): Table 't_gin.hahas' doesn't exist
           

果然沒有意外,如果不知道表名,gorm就按照傳入的參數結構體的名稱+s作為表名。

有些難以了解。。。

那怎麼指定表名呢?

有兩種方法,一種是在SQL執行時指定,一種是整個檔案範圍内的預設指定。

我們先看看第一種,執行時指定:

将db操作代碼修改如上後,再運作看一下

reflect: reflect.Value.SetInt using unaddressable value
           

還是報錯誤,參考了一下官方文檔,發現是參數傳錯了,傳參應該給位址,再修改一下:

然後重新執行,這次成功了。

go gin學習記錄3

我們看下資料庫裡的資料

mysql> select * from student;
+----+------+----------+
| id | name | birth    |
+----+------+----------+
|  5 | toms | 2002-5-1 |
+----+------+----------+
1 row in set (0.01 sec)
           

下面我們再看一下第二種指定表名的方法,檔案中全局指定:

我們修改一下student.go檔案,加入下列代碼:

type Tabler interface {
	TableName() string
}

func (Student) TableName() string {
	return "student"
}
           

相當于定義了一個接口并實作了其對應的方法,那我們看看這樣是否有效。

繼續修改插入操作的代碼

将sql操作中的表名指定去掉,然後我們再運作看看

go gin學習記錄3

運作成功,資料庫也有了對應的資料。說明這種方法也是可行的。

完善查詢操作

好的,基本的插入操作完善好了,下面來完善查詢操作

func (s *StudentController) GetStudentInfo(c *gin.Context) {
	name := c.Query("name")

	db, err := gorm.Open(mysql.Open(dsn))
	if err != nil {
		log.Panic(err.Error())
	}

	var stu Student
	result := db.Where("name=?", name).First(&stu)
	if result.Error == gorm.ErrRecordNotFound {
		c.JSON(http.StatusOK, gin.H{
			"code": 0,
			"data": "",
		})
	}
	if result.Error != nil {
		log.Panic(result.Error.Error())
	}

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

查詢操作完善之後,我們運作一下看看

go gin學習記錄3

非常好,一次成功。

查詢是日常開發中最常使用的功能,有許多中查詢的方式,以及各種限制條件,這些官方文檔裡都有詳細的說明,想了解具體的就去查一下官方文檔,這裡不做贅述。

完善更新操作

下面繼續完善更新操作,完善後的代碼如下:

func (s *StudentController) UpdateStudent(c *gin.Context) {
	name := c.PostForm("name")
	birth := c.PostForm("birth")
	
	db, err := gorm.Open(mysql.Open(dsn))
	if err != nil{
		log.Panic(err.Error())
	}
	
	var stu Student
	result := db.Model(&stu).Where("name=?", name).Update("birth", birth)
	if result.Error != nil {
		log.Panic(result.Error.Error())
	}
	
	c.JSON(http.StatusOK, gin.H{
		"code":0,
		"data": result.RowsAffected,
	})
}
           

我們繼續允許看看

go gin學習記錄3

好的,運作成功了

那資料庫中的資料确實更新了嗎?

mysql> select * from student;
+----+------+------------+
| id | name | birth      |
+----+------+------------+
|  5 | toms | 2002-5-1   |
|  6 | gaga | 2023-02-17 |
+----+------+------------+
2 rows in set (0.00 sec)
           

好的,确實更新了,那基本的更新操作也完成了。

擷取生成的SQL語句

有些情況下,我們可能想知道生成的sql是什麼樣子的,原生sql一目了然,而orm卻有些雲裡霧裡,就連記錄sql log都有些無從下手。

沒關系,我們找辦法把gorm生成的sql列印出來。

我們用最近的更新操作來做實驗,修改更新操作的db操作代碼如下:

var stu Student
	result := db.Model(&stu).Where("name=?", name).Update("birth", birth)
	ss := db.Session(&gorm.Session{DryRun: true}).Model(&stu).Where("name=?", name).Update("birth", birth).Statement
	log.Println(ss.SQL.String(), ss.Vars)

	if result.Error != nil {
		log.Panic(result.Error.Error())
	}
           

然後我們允許一下看看,就能看到在Terminal終端中列印出了生成的SQL語句:

[GIN-debug] Listening and serving HTTP on :8080
2023/02/17 23:09:56 UPDATE `student` SET `birth`=? WHERE name=? [2000-1-1 gaga]
[GIN] 2023/02/17 - 23:09:56 | 200 |   45.498834ms |             ::1 | POST     "/student/updateStudent"
           

那那那,gorm生成的sql也列印出來了,排查問題又簡單了一步。

好了,今天就到這兒。