這篇主要學(xué)習(xí)go項(xiàng)目中的項(xiàng)目結(jié)構(gòu)、項(xiàng)目規(guī)范等知識(shí),ROM采用的
database/sql的寫(xiě)法。
1.技術(shù)框架
利用的是ginweb框架,然后ROM層選用database/sql,安裝mysql驅(qū)動(dòng)。安裝方式如下:
//使用github上的gin托管地址
$ go get -u github.com/gin-gonic/gin
$ go get github.com/go-sql-driver/mysql
2.項(xiàng)目結(jié)構(gòu)如下
項(xiàng)目結(jié)構(gòu)分析:
- 1、
main.go主要是存放路由,啟動(dòng)項(xiàng)目; - 2、
router主要存放路由信息,然后返回一個(gè)router; - 3、
apis存放router的Handler函數(shù); - 4、
databases存放數(shù)據(jù)連接信息; - 5、
models存放數(shù)據(jù)模型,類似Java中POJO對(duì)象。
│ 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的一個(gè)別名,表示數(shù)據(jù)庫(kù)連接池
db "go/databases"
. "go/router"
)
func main() {
//當(dāng)整個(gè)程序完成之后關(guān)閉數(shù)據(jù)庫(kù)連接
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為一個(gè)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"
)
//因?yàn)槲覀冃枰谄渌胤绞褂肧qlDB這個(gè)變量,所以需要大寫(xiě)代表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())
}
//連接檢測(cè)
err = SqlDB.Ping()
if err != nil {
log.Fatal(err.Error())
}
}
使用sql.Open()方法會(huì)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接池db。這個(gè)地步不是數(shù)據(jù)庫(kù)連接,它是一個(gè)連接池,只有當(dāng)真正的數(shù)據(jù)庫(kù)通信的時(shí)候才創(chuàng)建連接。例如,這里的db.Ping()操作。db.SetMaxIdleConns(20)和db.SetMaxOpenConns(20)分別設(shè)置數(shù)據(jù)庫(kù)的空閑連接和最大打開(kāi)連接,即向Mysql服務(wù)端發(fā)出的所有連接的最大數(shù)目。
6.models中person.go代碼解釋
package models
import (
"log"
db "go/databases"
)
//定義person類型結(jié)構(gòu)
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
}
執(zhí)行非query操作,使用db的Exec方法,在MySQL中使用?做占位符。最后我們把插入后的Id返回給客戶端。
GetPersons方法解釋:
讀取MySQL的數(shù)據(jù)需要有一個(gè)綁定的過(guò)程,db.Query()方法返回一個(gè)rows對(duì)象,這個(gè)數(shù)據(jù)庫(kù)連接隨即轉(zhuǎn)移到這個(gè)對(duì)象,因此我們需要定義rows.Close()操作,然后創(chuàng)建一個(gè)[]Person的切片。
使用make,而不是直接使用
var persons []Person的聲明方式。還是有所差別的,使用make的方式,當(dāng)數(shù)組切片沒(méi)有元素的時(shí)候,Json會(huì)返回[]。如果直接聲明,json會(huì)返回null。
接下來(lái)就是使用rows對(duì)象的Next()方法,遍歷所查詢的數(shù)據(jù),一個(gè)個(gè)綁定到person對(duì)象上,最后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,
})
}
其實(shí),整個(gè)項(xiàng)目的結(jié)構(gòu)和CRUD操作跟Java中的思想比較類似,應(yīng)該很容易上手。需要注意一點(diǎn)的是,如果需要將整個(gè)項(xiàng)目運(yùn)行起來(lái),項(xiàng)目的路徑一定Gopath路徑:F:\Go\Project\src;


項(xiàng)目啟動(dòng)結(jié)果如下:

熟悉了database/sql的寫(xiě)法后,下一步就是學(xué)習(xí)ROM框架gorm的寫(xiě)法,進(jìn)而學(xué)習(xí)Docker進(jìn)行部署。