這篇主要學習go項目中的項目結構、項目規范等知識,ROM采用的
database/sql
的寫法。
1.技術框架
利用的是gin
web框架,然后ROM層選用database/sql
,安裝mysql
驅動。安裝方式如下:
//使用github上的gin托管地址 $ go get -u github.com/gin-gonic/gin $ go get github.com/go-sql-driver/mysql
2.項目結構如下
項目結構分析:
- 1、
main.go
主要是存放路由,啟動項目; - 2、
router
主要存放路由信息,然后返回一個router
; - 3、
apis
存放router
的Handler
函數; - 4、
databases
存放數據連接信息; - 5、
models
存放數據模型,類似Java中POJO
對象。
│ main.go
│
├─.idea
│ │ go.iml
│ │ misc.xml
│ │ modules.xml
│ │ workspace.xml
│ │
│ └─inspectionProfiles
├─apis
│ person.go
│
├─databases
│ mysql.go
│
├─models
│ person.go
│
└─router
router.go

3.main.go代碼解釋
package main import ( //這里講db作為go/databases的一個別名,表示數據庫連接池 db "go/databases" . "go/router" ) func main() { //當整個程序完成之后關閉數據庫連接 defer db.SqlDB.Close() router := InitRouter() router.Run(":8080") }
4.router.go代碼解釋
package router import ( "github.com/gin-gonic/gin" ."go/apis" ) func InitRouter() *gin.Engine { router := gin.Default() //IndexApi為一個Handler router.GET("/", IndexApi) router.POST("/person", AddPersonApi) router.GET("/persons", GetPersonsApi) router.GET("/person/:id", GetPersonApi) router.PUT("/person/:id", ModPersonApi) router.DELETE("/person/:id", DelPersonApi) return router }
5.mysql.go代碼解釋
package databases import ( "database/sql" _ "github.com/go-sql-driver/mysql" "log" ) //因為我們需要在其他地方使用SqlDB這個變量,所以需要大寫代表public var SqlDB *sql.DB //初始化方法 func init() { var err error SqlDB, err = sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test?parseTime=true") if err != nil { log.Fatal(err.Error()) } //連接檢測 err = SqlDB.Ping() if err != nil { log.Fatal(err.Error()) } }
使用sql.Open()
方法會創建一個數據庫連接池db
。這個地步不是數據庫連接,它是一個連接池,只有當真正的數據庫通信的時候才創建連接。例如,這里的db.Ping()
操作。db.SetMaxIdleConns(20)
和db.SetMaxOpenConns(20)
分別設置數據庫的空閑連接和最大打開連接,即向Mysql服務端發出的所有連接的最大數目。
6.models中person.go代碼解釋
package models import ( "log" db "go/databases" ) //定義person類型結構 type Person struct { Id int `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } func (p *Person) AddPerson() (id int64, err error) { rs, err := db.SqlDB.Exec("INSERT INTO person(first_name, last_name) VALUES (?, ?)", p.FirstName, p.LastName) if err != nil { return } id, err = rs.LastInsertId() return } func (p *Person) GetPersons() (persons []Person, err error) { persons = make([]Person, 0) rows, err := db.SqlDB.Query("SELECT id, first_name, last_name FROM person") defer rows.Close() if err != nil { return } for rows.Next() { var person Person rows.Scan(&person.Id, &person.FirstName, &person.LastName) persons = append(persons, person) } if err = rows.Err(); err != nil { return } return } func (p *Person) GetPerson() (person Person, err error) { err = db.SqlDB.QueryRow("SELECT id, first_name, last_name FROM person WHERE id=?", p.Id).Scan( &person.Id, &person.FirstName, &person.LastName, ) return } func (p *Person) ModPerson() (ra int64, err error) { stmt, err := db.SqlDB.Prepare("UPDATE person SET first_name=?, last_name=? WHERE id=?") defer stmt.Close() if err != nil { return } rs, err := stmt.Exec(p.FirstName, p.LastName, p.Id) if err != nil { return } ra, err = rs.RowsAffected() return } func (p *Person) DelPerson() (ra int64, err error) { rs, err := db.SqlDB.Exec("DELETE FROM person WHERE id=?", p.Id) if err != nil { log.Fatalln(err) } ra, err = rs.RowsAffected() return }
執行非query
操作,使用db
的Exec
方法,在MySQL中使用?
做占位符。最后我們把插入后的Id
返回給客戶端。
GetPersons方法解釋:
讀取MySQL的數據需要有一個綁定的過程,db.Query()
方法返回一個rows對象,這個數據庫連接隨即轉移到這個對象,因此我們需要定義rows.Close()
操作,然后創建一個[]Person
的切片。
使用make,而不是直接使用
var persons []Person
的聲明方式。還是有所差別的,使用make的方式,當數組切片沒有元素的時候,Json會返回[]
。如果直接聲明,json會返回null
。
接下來就是使用rows
對象的Next()
方法,遍歷所查詢的數據,一個個綁定到person
對象上,最后append
到person
切片。
7.apis中的person.go代碼解釋
package apis import ( "net/http" "log" "fmt" "strconv" "github.com/gin-gonic/gin" ."go/models" ) func IndexApi(c *gin.Context) { c.String(http.StatusOK, "It works") } func AddPersonApi(c *gin.Context) { firstName := c.Request.FormValue("first_name") lastName := c.Request.FormValue("last_name") p := Person{FirstName: firstName, LastName: lastName} ra, err := p.AddPerson() if err != nil { log.Fatalln(err) } msg := fmt.Sprintf("insert successful %d", ra) c.JSON(http.StatusOK, gin.H{ "msg": msg, }) } func GetPersonsApi(c *gin.Context) { var p Person persons, err := p.GetPersons() if err != nil { log.Fatalln(err) } c.JSON(http.StatusOK, gin.H{ "persons": persons, }) } func GetPersonApi(c *gin.Context) { cid := c.Param("id") id, err := strconv.Atoi(cid) if err != nil { log.Fatalln(err) } p := Person{Id: id} person, err := p.GetPerson() if err != nil { log.Fatalln(err) } c.JSON(http.StatusOK, gin.H{ "person": person, }) } func ModPersonApi(c *gin.Context) { cid := c.Param("id") id, err := strconv.Atoi(cid) if err != nil { log.Fatalln(err) } p := Person{Id: id} err = c.Bind(&p) if err != nil { log.Fatalln(err) } ra, err := p.ModPerson() if err != nil { log.Fatalln(err) } msg := fmt.Sprintf("Update person %d successful %d", p.Id, ra) c.JSON(http.StatusOK, gin.H{ "msg": msg, }) } func DelPersonApi(c *gin.Context) { cid := c.Param("id") id, err := strconv.Atoi(cid) if err != nil { log.Fatalln(err) } p := Person{Id: id} ra, err := p.DelPerson() if err != nil { log.Fatalln(err) } msg := fmt.Sprintf("Delete person %d successful %d", id, ra) c.JSON(http.StatusOK, gin.H{ "msg": msg, }) }
其實,整個項目的結構和CRUD操作跟Java中的思想比較類似,應該很容易上手。需要注意一點的是,如果需要將整個項目運行起來,項目的路徑一定Gopath
路徑:F:\Go\Project\src
;


項目啟動結果如下:

熟悉了database/sql
的寫法后,下一步就是學習ROM框架gorm
的寫法,進而學習Docker進行部署。