来源于B站视频,知识点整理的第四篇包含数据库编程及路由部分
课程视频
如需查看上篇内容请看:
Go-Web基础入门教程(含源码)【第三篇】
Web是基于http协议的一个服务,Go语言里面提供了一个完善的net/http包,通过http包可以很方便的就搭建起来一个可以运行的Web服务。
补充 - Go数据库编程如果没有下载数据库相关的引用,则需要在控制台输入
go get github.com/go-sql-driver/mysql
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
const (
server = "127.0.0.1"
port = 3306
user = "root"
password = "root"
database = "eesy"
)
func main() {
// 需要改成你自己数据库的配置
db, _ := sql.Open("mysql", "db:db@(127.0.0.1:3306)/dbname")
err := db.Ping()
if err != nil {
log.Fatalln(err.Error())
}
defer db.Close()
ctx := context.Background()
err = db.PingContext(ctx)
if err != nil {
log.Fatalln(err.Error())
}
fmt.Println("Connected!")
}
想要连接SQL数据库,首先需要加载目标数据库的驱动,驱动中包含着与该数据库交互的逻辑.
- sql.Open()
- 数据库驱动的名称
- 数据源的名称
- 得到一个指向sql.DB这个strcut的指针
- sql.DB对象对于多个 goroutine 并发使用是安全的
sql.DB是用来操作数据库的,实际上代表了0个或者多个底层连接池,这些连接由SQL包来维护,SQL包会自动创建和释放这些连接.
如何获取驱动?
正常的做法是通过sql.Register()函数,数据库驱动的名称以及一个实现了driver.Driver接口的struct来注册数据库的驱动.例如:
- sql.Register("mysql", &drv{})
驱动的自动注册概念
- 当 go-sql-driver/mysql包被引入的时候,它的init函数会运行并且进行自我注册
- 在引入包的开始位置使用_ 是因为我们不直接使用数据库驱动,只使用database/sql
- 这样设计的好处在于即使驱动需要升级也无需改变代码
- Go语言没有提供官方的数据库驱动,所有的数据库驱动都是第三方驱动,它们都遵循sql.driver包里面定义的接口
- sql.DB类型上用于查询的方法:
- Query把查询SQL语句发送到数据库
- QueryRow适用于只有一条记录的方法
- QueryContext:携带上下文信息
- QueryRowContext:携带上下文信息
Query方法:
QueryRow方法:
案例使用的数据库表结构如下:
表中的数据如下:
案例一:查询演练
案例总共包含三个文件分别是:model.go + service.go + main.go
// model.go
package main
type user struct {
Id int
username string
pwd string
}
// service.go
package main
import (
"log"
)
// 查询单一结果
func getOne(id int) (u user, err error) {
u = user{}
log.Println(db == nil)
err = db.QueryRow("SELECt id, username, password FROM user WHERe id = ?", id).Scan(
&u.Id, &u.username, &u.pwd)
return
}
// 查询多个结果
func getMany(id int) (us []user, err error) {
// 返回类型为 sql.Rows - 可以理解为一个迭代器
rows, err := db.Query("SELECt id, username, password FROM user WHERe id > ?", id)
for rows.Next() {
u := user{}
err = rows.Scan(&u.Id, &u.username, &u.pwd)
if err != nil {
log.Fatalln(err.Error())
}
us = append(us, u)
}
return
}
// main.go
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func main() {
// 如果里面使用 db, err := ... 会导致内部同局部变量覆盖全局变量
var err error
// 需要改成你自己数据库的配置
db, err = sql.Open("mysql", "db:db@(127.0.0.1:3306)/dbname")
if err != nil {
log.Fatalln(err.Error())
}
ctx := context.Background()
err = db.PingContext(ctx)
if err != nil {
log.Fatalln(err.Error())
}
fmt.Println("Connected!")
log.Println(db == nil)
one, err := getOne(2)
fmt.Println(one)
many, err := getMany(0)
fmt.Println(many)
}
SQL - 更新及删除
案例:需要在 service.go 中添加如下方法:
func (u *user) Update() (err error) {
_, err = db.Exec("UPDATE user SET username = ? WHERe id = ?", u.username, u.Id)
if err != nil {
log.Fatalln(err.Error())
}
return
}
func (u *user) Delete() (err error) {
_, err = db.Exec("DELETE FROM user WHERe id = ?", u.Id)
if err != nil {
log.Fatalln(err.Error())
}
return
}
SQL - 其他补充知识
func (u *user) Insert() (err error) {
// 建立 SQL 语句模板
statement := `INSERT INTO user (id, username, password) VALUES (?, ?, ?)`
stmt, err := db.Prepare(statement)
defer stmt.Close()
if err != nil {
log.Fatalln(err.Error())
}
stmt.QueryRow(u.Id, u.username, u.pwd)
fmt.Println("Insert success!")
return
}
08 - 路由
Controller层介绍
案例:抽取Controller层
当前案例的结构如下:
- main.go
package main
import (
"go_code/project03_go_web/demo08router/controller"
"net/http"
)
func main() {
controller.RegisterRoutes()
http.ListenAndServe("localhost:8888", nil)
}
- html
{{ define "content" }}
About
{{ . }}
{{ end }}
{{ define "content" }}
Home
{{ . }}
{{ end }}
{{ define "layout"}}
Layout
{{ template "content" .}}
{{ end }}
- controller
// about.go
package controller
import (
"html/template"
"net/http"
)
func registerAboutRoutes() {
http.HandleFunc("/about", handleAbout)
}
func handleAbout(rw http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "about.html")
// 因为在模板里面使用 define 重新命名了模板,所以使用的时候需要传入定义的名称.
t.ExecuteTemplate(rw, "layout", "")
}
// home.go
package controller
import (
"html/template"
"net/http"
)
func registerHomeRoutes() {
http.HandleFunc("/home", handleHome)
}
func handleHome(rw http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "home.html")
// 因为在模板里面使用 define 重新命名了模板,所以使用的时候需要传入定义的名称.
t.ExecuteTemplate(rw, "layout", "Hello!")
}
// controller.go
package controller
func RegisterRoutes() {
registerHomeRoutes()
registerAboutRoutes()
}
路由参数
- main.go
package main
import (
"go_code/project03_go_web/demo08router/controller"
"net/http"
)
func main() {
controller.RegisterRoutes()
http.ListenAndServe("localhost:8888", nil)
}
- html
{{ define "content" }}
About
{{ . }}
{{ end }}
{{ define "content" }}
Home
{{ . }}
{{ end }}
{{ define "layout"}}
Layout
{{ template "content" .}}
{{ end }}
{{ define "content"}}
Company: {{ . }}
{{ end }}
{{ define "content"}}
All Companies
- Microsoft
- Google
- Apple
{{ end }}
- controller
// about.go
package controller
import (
"html/template"
"net/http"
)
func registerAboutRoutes() {
http.HandleFunc("/about", handleAbout)
}
func handleAbout(rw http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "about.html")
// 因为在模板里面使用 define 重新命名了模板,所以使用的时候需要传入定义的名称.
t.ExecuteTemplate(rw, "layout", "")
}
// home.go
package controller
import (
"html/template"
"net/http"
)
func registerHomeRoutes() {
http.HandleFunc("/home", handleHome)
}
func handleHome(rw http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "home.html")
// 因为在模板里面使用 define 重新命名了模板,所以使用的时候需要传入定义的名称.
t.ExecuteTemplate(rw, "layout", "Hello!")
}
// controller.go
package controller
func RegisterRoutes() {
registerHomeRoutes()
registerAboutRoutes()
registerCompanyRoutes()
}
// company.go
package controller
import (
"fmt"
"net/http"
"regexp"
"strconv"
"text/template"
)
func registerCompanyRoutes() {
http.HandleFunc("/companies", handlerCompanies)
http.HandleFunc("/companies/", handlerCompany)
}
func handlerCompanies(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "companies.html")
t.ExecuteTemplate(w, "layout", nil)
}
func handlerCompany(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("layout.html", "company.html")
pattern, _ := regexp.Compile(`/companies/(d+)`)
matches := pattern.FindStringSubmatch(r.URL.Path)
if len(matches) > 0 {
fmt.Println(matches[0])
companyID, _ := strconv.Atoi(matches[1])
t.ExecuteTemplate(w, "layout", companyID)
} else {
w.WriteHeader(http.StatusNotFound)
}
}
第三方路由器
- gorilla/mux:灵活性高、功能强大、性能相对差一些
- httprouter:注重性能、功能简单



