環境
環境: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,
})
}
好的,啟動項目,調用一下看看結果
出錯了。
看一下終端的資訊:
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
還是報錯誤,參考了一下官方文檔,發現是參數傳錯了,傳參應該給位址,再修改一下:
然後重新執行,這次成功了。
我們看下資料庫裡的資料
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操作中的表名指定去掉,然後我們再運作看看
運作成功,資料庫也有了對應的資料。說明這種方法也是可行的。
完善查詢操作
好的,基本的插入操作完善好了,下面來完善查詢操作
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,
})
}
查詢操作完善之後,我們運作一下看看
非常好,一次成功。
查詢是日常開發中最常使用的功能,有許多中查詢的方式,以及各種限制條件,這些官方文檔裡都有詳細的說明,想了解具體的就去查一下官方文檔,這裡不做贅述。
完善更新操作
下面繼續完善更新操作,完善後的代碼如下:
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,
})
}
我們繼續允許看看
好的,運作成功了
那資料庫中的資料确實更新了嗎?
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也列印出來了,排查問題又簡單了一步。
好了,今天就到這兒。