本节主要内容:

1. http编程
2. mysql使用

1. http编程

(1)http编程分析

  • Go原生支持http,import(“net/http”)
  • Go的http服务性能和nginx比较接近
  • 几行代码就可以实现一个web服务

关于HTTP,TCP/IP相关知识可以看系列博客 https://www.jianshu.com/p/dfbac2ff2657

首先来看一个最简单的http服务器:

 package main

 import (
"io"
"log"
"net/http"
) func main() {
// Hello world, the web server helloHandler := func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Hello, world!\n")
} http.HandleFunc("/hello", helloHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}

http server(hello world)

执行结果:

几个函数及接口定义:

  • HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

HandleFunc registers the handler function for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns 
are matched.
 package main

 import (
"io"
"log"
"net/http"
) func main() {
h1 := func(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello from a HandleFunc #1!\n")
}
h2 := func(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello from a HandleFunc #2!\n")
} http.HandleFunc("/", h1)
http.HandleFunc("/endpoint", h2) log.Fatal(http.ListenAndServe(":8080", nil))
}

HandleFunc

  • ResponseWriter接口
A ResponseWriter interface is used by an HTTP handler to construct an HTTP response.
A ResponseWriter may not be used after the Handler.ServeHTTP method has returned.
 type ResponseWriter interface {
// Header returns the header map that will be sent by
// WriteHeader. The Header map also is the mechanism with which
// Handlers can set HTTP trailers.
//
// Changing the header map after a call to WriteHeader (or
// Write) has no effect unless the modified headers are
// trailers.
//
// There are two ways to set Trailers. The preferred way is to
// predeclare in the headers which trailers you will later
// send by setting the "Trailer" header to the names of the
// trailer keys which will come later. In this case, those
// keys of the Header map are treated as if they were
// trailers. See the example. The second way, for trailer
// keys not known to the Handler until after the first Write,
// is to prefix the Header map keys with the TrailerPrefix
// constant value. See TrailerPrefix.
//
// To suppress automatic response headers (such as "Date"), set
// their value to nil.
Header() Header // Write writes the data to the connection as part of an HTTP reply.
//
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType. Additionally, if the total size of all written
// data is under a few KB and there are no Flush calls, the
// Content-Length header is added automatically.
//
// Depending on the HTTP protocol version and the client, calling
// Write or WriteHeader may prevent future reads on the
// Request.Body. For HTTP/1.x requests, handlers should read any
// needed request body data before writing the response. Once the
// headers have been flushed (due to either an explicit Flusher.Flush
// call or writing enough data to trigger a flush), the request body
// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
// handlers to continue to read the request body while concurrently
// writing the response. However, such behavior may not be supported
// by all HTTP/2 clients. Handlers should read before writing if
// possible to maximize compatibility.
Write([]byte) (int, error) // WriteHeader sends an HTTP response header with the provided
// status code.
//
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
//
// The provided code must be a valid HTTP 1xx-5xx status code.
// Only one header may be written. Go does not currently
// support sending user-defined 1xx informational headers,
// with the exception of 100-continue response header that the
// Server sends automatically when the Request.Body is read.
WriteHeader(statusCode int)
}

type ResponseWriter interface

  • ListenAndServe
func ListenAndServe(addr string, handler Handler) error
ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle requests on incoming connections.
Accepted connections are configured to enable TCP keep-alives. The handler is typically nil, in which case the DefaultServeMux is used. ListenAndServe always returns a non-nil error.

具体实现过程需要分析:后续补上。

(2)http常见请求方法
    1)Get请求
    2)Post请求
    3)Put请求
    4)Delete请求
    5)Head请求

 package main

 import (
"fmt"
"io/ioutil"
"net/http"
) func main() {
res, err := http.Get("https://www.baidu.com/")
if err != nil {
fmt.Println("get err:", err)
return
} data, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("get data err:", err)
return
} fmt.Println(string(data))
}

Get请求示例

 package main

 import (
"fmt"
"net/http"
) var url = []string{
"http://www.baidu.com",
"http://google.com",
"http://taobao.com",
} func main() { for _, v := range url {
resp, err := http.Head(v)
if err != nil {
fmt.Printf("head %s failed, err:%v\n", v, err)
continue
} fmt.Printf("head succ, status:%v\n", resp.Status)
}
}

Head请求示例

  • Get
 func (c *Client) Get(url string) (resp *Response, err error)

 Get issues a GET to the specified URL. If the response is one of the following redirect codes, Get follows the redirect after calling the Client's CheckRedirect function:

  (Moved Permanently)
(Found)
(See Other)
(Temporary Redirect)
(Permanent Redirect)
An error is returned if the Client's CheckRedirect function fails or if there was an HTTP protocol error. A non-2xx response doesn't cause an error. Any returned error will be of type *url.Error. The url.Error value's Timeout method will report true if request timed out or was canceled. When err is nil, resp always contains a non-nil resp.Body. Caller should close resp.Body when done reading from it. To make a request with custom headers, use NewRequest and Client.Do.

Get

  • Head
 func Head(url string) (resp *Response, err error)

 func (c *Client) Head(url string) (resp *Response, err error)
Head issues a HEAD to the specified URL. If the response is one of the following redirect codes, Head follows the redirect after calling the Client's CheckRedirect function: (Moved Permanently)
(Found)
(See Other)
(Temporary Redirect)
(Permanent Redirect)

Head

  • Response
 type Response struct {
Status string // e.g. "200 OK"
StatusCode int // e.g. 200
Proto string // e.g. "HTTP/1.0"
ProtoMajor int // e.g. 1
ProtoMinor int // e.g. 0 // Header maps header keys to values. If the response had multiple
// headers with the same key, they may be concatenated, with comma
// delimiters. (RFC 7230, section 3.2.2 requires that multiple headers
// be semantically equivalent to a comma-delimited sequence.) When
// Header values are duplicated by other fields in this struct (e.g.,
// ContentLength, TransferEncoding, Trailer), the field values are
// authoritative.
//
// Keys in the map are canonicalized (see CanonicalHeaderKey).
Header Header // Body represents the response body.
//
// The response body is streamed on demand as the Body field
// is read. If the network connection fails or the server
// terminates the response, Body.Read calls return an error.
//
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
//
// As of Go 1.12, the Body will be also implement io.Writer
// on a successful "101 Switching Protocols" responses,
// as used by WebSockets and HTTP/2's "h2c" mode.
Body io.ReadCloser // ContentLength records the length of the associated content. The
// value -1 indicates that the length is unknown. Unless Request.Method
// is "HEAD", values >= 0 indicate that the given number of bytes may
// be read from Body.
ContentLength int64 // Contains transfer encodings from outer-most to inner-most. Value is
// nil, means that "identity" encoding is used.
TransferEncoding []string // Close records whether the header directed that the connection be
// closed after reading Body. The value is advice for clients: neither
// ReadResponse nor Response.Write ever closes a connection.
Close bool // Uncompressed reports whether the response was sent compressed but
// was decompressed by the http package. When true, reading from
// Body yields the uncompressed content instead of the compressed
// content actually set from the server, ContentLength is set to -1,
// and the "Content-Length" and "Content-Encoding" fields are deleted
// from the responseHeader. To get the original response from
// the server, set Transport.DisableCompression to true.
Uncompressed bool // Go 1.7 // Trailer maps trailer keys to values in the same
// format as Header.
//
// The Trailer initially contains only nil values, one for
// each key specified in the server's "Trailer" header
// value. Those values are not added to Header.
//
// Trailer must not be accessed concurrently with Read calls
// on the Body.
//
// After Body.Read has returned io.EOF, Trailer will contain
// any trailer values sent by the server.
Trailer Header // Request is the request that was sent to obtain this Response.
// Request's Body is nil (having already been consumed).
// This is only populated for Client requests.
Request *Request // TLS contains information about the TLS connection on which the
// response was received. It is nil for unencrypted responses.
// The pointer is shared between responses and should not be
// modified.
TLS *tls.ConnectionState // Go 1.3
}

Response

  • http 常见状态码
http.StatusContinue =
http.StatusOK =
http.StatusFound =
http.StatusBadRequest =
http.StatusUnauthorized =
http.StatusForbidden =
http.StatusNotFound =
http.StatusInternalServerError =

(3)表单处理

 package main
import (
"io"
"net/http"
) const form = `<html><body><form action="#" method="post" name="bar">
<input type="text" name="in"/>
<input type="text" name="in"/>
<input type="submit" value="Submit"/>
</form></html></body>` func SimpleServer(w http.ResponseWriter, request *http.Request) {
io.WriteString(w, "<h1>hello, world</h1>")
} func FormServer(w http.ResponseWriter, request *http.Request) {
w.Header().Set("Content-Type", "text/html")
switch request.Method {
case "GET":
io.WriteString(w, form)
case "POST":
request.ParseForm()
io.WriteString(w, request.Form["in"][])
io.WriteString(w, "\n")
io.WriteString(w, request.FormValue("in"))
}
}
func main() {
http.HandleFunc("/test1", SimpleServer)
http.HandleFunc("/test2", FormServer)
if err := http.ListenAndServe(":8088", nil); err != nil {
}
}

form表单处理

执行结果:

url: test1

url: test2(第一次请求是GET,第二次是POST请求)

GET请求:

POST请求:

(4)模板

1)替换 {{.字段名}}

模板中的点(.)为结构体中的p

 package main

 import (
"fmt"
"os"
"text/template"
) type Person struct {
Name string
Age string
Title string
} func main() {
t, err := template.ParseFiles("./index.html")
if err != nil {
fmt.Println("parse file err:", err)
return
}
p := Person{Name: "Mary", Age: "", Title:"My blog"}
if err := t.Execute(os.Stdout, p); err != nil {
fmt.Println("There was an error:", err.Error())
}
}

template.go

 <html>
<head>
<title>{{.Title}}</title>
</head>
<body>
<p> hello, {{.Name}}</p>
<p> {{.}}</p>
</body>
</html>

index.html

2)if判断

 package main

 import (
"fmt"
"os"
"text/template"
) type Person struct {
Name string
Age int
Title string
} func main() {
t, err := template.ParseFiles("./index2.html")
if err != nil {
fmt.Println("parse file err:", err)
return
}
p := Person{Name: "Mary", Age: , Title:"My blog"}
if err := t.Execute(os.Stdout, p); err != nil {
fmt.Println("There was an error:", err.Error())
}
}

template2.go

 <html>
<head>
<title>{{.Title}}</title>
</head>
<body>
{{if ge .Age }}
<p>hello, old man, {{.Name}}</p>
{{else}}
<p>hello,young man, {{.Name}}</p>
{{end}}
</body>
</html>

index2.html

if常见操作符:

not 非 {{if not .condition}} {{end}}
and 与 {{if and .condition1 .condition2}} {{end}}
or 或 {{if or .condition1 .condition2}} {{end}}
eq 等于 {{if eq .var1 .var2}} {{end}}
ne 不等于 {{if ne .var1 .var2}} {{end}}
lt 小于 (less than) {{if lt .var1 .var2}} {{end}}
le 小于等于 {{if le .var1 .var2}} {{end}}
gt 大于 {{if gt .var1 .var2}} {{end}}
ge 大于等于 {{if ge .var1 .var2}} {{end}}

(3)点(.)

{{.}}

<html>
<head>
</head>
<body>
<p>hello, old man, {{.}}</p>
</body>
</html>

(4)with

with语言在Python中可以开启一个上下文环境。对于go模板,with语句类似,其含义就是创建一个封闭的作用域,在其范围内,可以使用.action,而与外面的.无关,只与with的参数有关:

{{ with arg }}
此时的点 . 就是arg
{{ end }}
 package main

 import (
"fmt"
"os"
"text/template"
) type Person struct {
Name string
Age int
Title string
} func main() {
t, err := template.ParseFiles("./index3.html")
if err != nil {
fmt.Println("parse file err:", err)
return
}
p := Person{Name: "Mary", Age: , Title:"My blog"}
if err := t.Execute(os.Stdout, p); err != nil {
fmt.Println("There was an error:", err.Error())
}
}

template3.go

 <html>
<head>
</head>
<body>
{{with "my with"}}
<p>in: {{.}}</p>
{{end}}
<p>out: {{.}}</p>
</body>
</html>

index3.html

执行结果:

<html>
<head>
</head>
<body> <p>in: my with</p> <p>out: {Mary 17 My blog}</p>
</body>
</html>

(5)循环

{{range.}} ... {{end }}

 <html>
<head>
</head>
<body>
{{range .}}
{{if gt .Age 18}}
<p>hello, old man, {{.Name}}</p>
{{else}}
<p>hello,young man, {{.Name}}</p>
{{end}}
{{end}}
</body>
</html>

index5.html

 package main

 import (
"fmt"
"os"
"text/template"
) type Person struct {
Name string
Age int
Title string
} func main() {
t, err := template.ParseFiles("./index5.html")
if err != nil {
fmt.Println("parse file err:", err)
return
} p := []Person {
Person {
Name: "Mary",
Age: ,
Title:"My blog",
},
Person {
Name: "Sara",
Age: ,
Title:"My blog",
},
Person {
Name: "Jack",
Age: ,
Title:"My blog",
},
} if err := t.Execute(os.Stdout, p); err != nil {
fmt.Println("There was an error:", err.Error())
}
}

template5.go

执行结果:

<html>
<head>
</head>
<body>
<p>hello,young man, Mary</p>
<p>hello,young man, Sara</p>
<p>hello, old man, Jack</p>
</body>
</html>

关于Go的模板更详细的可以看这篇博客:https://www.jianshu.com/p/05671bab2357

2. mysql使用

(1)连接mysql

1)安装第三方驱动库

首先需要安装go驱动mysql数据库的API库:

go get github.com/go-sql-driver/mysql

Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能:

  • sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作.
  • sql.DB 为我们管理数据库连接池

需要注意的是,sql.DB表示操作数据库的抽象访问接口,而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,如果你没有把连接释放回连接池,会导致过多连接使系统资源耗尽。

sqlx使用指南:

sqlx是一个go语言包,在内置database/sql包之上增加了很多扩展,简化数据库操作代码的书写。
    由于database/sql接口是sqlx的子集,当前文档中所有关于database/sql的用法同样用于sqlx。

需要安装下面的API库:

go get github.com/jmoiron/sqlx

sqlx的详细使用可以看:http://jmoiron.github.io/sqlx/

2)操作mysql数据库

首先连接mysql数据库并创建测试数据库及测试表(mysql数据库在Linux Centos6.5上安装):

创建数据库并切换到创建的数据库中:

mysql> CREATE DATABASE mydb;
Query OK, row affected (0.02 sec)
mysql> USE mydb;
Database changed

创建测试表:

CREATE TABLE person (
user_id int primary key auto_increment,
username varchar(),
sex varchar(),
email varchar()
); CREATE TABLE place (
country varchar(),
city varchar(),
telcode int
);

导入mysql数据库驱动:

import (
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)

通常来说, 不应该直接使用驱动所提供的方法, 而是应该使用 sql.DB, 因此在导入 mysql 驱动时, 这里使用了匿名导入的方式(在包路径前添加 _), 当导入了一个数据库驱动后, 此驱动会自行初始化并注册自己到Golang的database/sql上下文中, 因此我们就可以通过 database/sql 包提供的方法访问数据库了.

连接数据库:

const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
}

我们将连接数据库接口写到init()函数中,在程序刚开始执行时,会先初始化init()函数,然后将结果存储到Db当中。

通过调用sqlx.Open函数返回一个DB结构体指针; sqlx.Open函数原型如下(对sql.Open做了一层封装):

// Open is the same as sql.Open, but returns an *sqlx.DB instead.
func Open(driverName, dataSourceName string) (*DB, error) {
db, err := sql.Open(driverName, dataSourceName)
if err != nil {
return nil, err
}
return &DB{DB: db, driverName: driverName, Mapper: mapper()}, err
}

driverName: 使用的驱动名. 这个名字其实就是数据库驱动注册到 database/sql 时所使用的名字。
    dataSourceName: 数据库连接信息,这个连接包含了数据库的用户名, 密码, 数据库主机以及需要连接的数据库名等信息。

  • sql.Open并不会立即建立一个数据库的网络连接, 也不会对数据库链接参数的合法性做检验, 它仅仅是初始化一个sql.DB对象. 当真正进行第一次数据库查询操作时, 此时才会真正建立网络连接;
  • sql.DB表示操作数据库的抽象接口的对象,但不是所谓的数据库连接对象,sql.DB对象只有当需要使用时才会创建连接,如果想立即验证连接,需要用Ping()方法;
  • sql.Open返回的sql.DB对象是协程并发安全的.
  • sql.DB的设计就是用来作为长连接使用的。不要频繁Open, Close。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象Open。如果需要短连接,那么把DB作为参数传入function,而不要在function中Open, Close。

上面代码见:github.com\jmoiron\sqlx\sqlx.go,下同。

Go操作Mysql更多接口见官网:https://golang.org/pkg/database/sql/

DB结构体:

// DB is a wrapper around sql.DB which keeps track of the driverName upon Open,
// used mostly to automatically bind named queries using the right bindvars.
type DB struct {
*sql.DB
driverName string
unsafe bool
Mapper *reflectx.Mapper
}
  • insert操作
 package main

 import (
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
) type Person struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
} type Place struct {
Country string `db:"country"`
City string `db:"city"`
TelCode int `db:"telcode"`
} const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
} func main() {
insert_cmd := `insert into person(username, sex, email)values(?, ?, ?)`
r, err := Db.Exec(insert_cmd, "stu002", "femal", "stu01@qq.com")
if err != nil {
fmt.Println("exec failed, ", err)
return
} id, err := r.LastInsertId()
if err != nil {
fmt.Println("exec failed, ", err)
return
} fmt.Println("insert succ:", id)
}

insert

LastInsertId() (int64, error)
// LastInsertId returns the integer generated by the database
// in response to a command. Typically this will be from an
// "auto increment" column when inserting a new row. Not all
// databases support this feature, and the syntax of such
// statements varies.
  • Select 操作
 package main

 import (
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
) type Person struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
} type Place struct {
Country string `db:"country"`
City string `db:"city"`
TelCode int `db:"telcode"`
} const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
} func main() { var person []Person
select_cmd := `select user_id, username, sex, email from person where user_id=?`
err := Db.Select(&person, select_cmd, )
if err != nil {
fmt.Println("exec failed, ", err)
return
} fmt.Println("select succ:", person) //select succ: [{1 stu002 femal stu01@qq.com}]
}

select

  • update操作
 package main

 import (
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
) type Person struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
} type Place struct {
Country string `db:"country"`
City string `db:"city"`
TelCode int `db:"telcode"`
} const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
} func main() {
//select
var person []Person
select_cmd := `select user_id, username, sex, email from person where user_id=?`
err := Db.Select(&person, select_cmd, )
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person) //select succ: [{1 stu001 femal stu01@qq.com}]
//update
_, err = Db.Exec("update person set username=? where user_id=?", "Jack", )
if err != nil {
fmt.Println("exec failed, ", err)
return
}
//select
var person2 []Person
err = Db.Select(&person2, "select * from person")
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person2) //select succ: [{1 Jack femal stu01@qq.com} {2 stu002 femal stu01@qq.com}]
}

update

  • Delete 操作
 package main

 import (
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
) type Person struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
} type Place struct {
Country string `db:"country"`
City string `db:"city"`
TelCode int `db:"telcode"`
} const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
} func main() {
//select
var person []Person
err := Db.Select(&person, "select * from person")
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person) //select succ: [{1 Jack femal stu01@qq.com} {2 stu002 femal stu01@qq.com}]
//delete
_, err = Db.Exec("delete from person where user_id=?", )
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("delete succ")
//select
var person2 []Person
err = Db.Select(&person2, "select * from person")
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person2) //select succ: [{2 stu002 femal stu01@qq.com}]
}

delete

  • 事物
tx, err := db.Begin()
err = tx.Exec(...)
err = tx.Commit()
 package main

 import (
"fmt"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
) type Person struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
} type Place struct {
Country string `db:"country"`
City string `db:"city"`
TelCode int `db:"telcode"`
} const (
userName = "root"
password = "root"
ip = "192.168.30.134"
port = ""
dbName = "mydb"
) var Db *sqlx.DB func init() {
path := strings.Join([]string{userName, ":", password, "@tcp(",ip, ":", port, ")/", dbName, "?charset=utf8"}, "")
database, err := sqlx.Open("mysql", path)
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
} func main() {
//select
var person []Person
err := Db.Select(&person, "select * from person")
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person) //select succ: [{2 stu002 femal stu01@qq.com}]
//begin transactions
tx, err := Db.Begin()
if err != nil {
fmt.Println("Failed to begin transactions")
return
}
//insert
insert_cmd := `insert into person(username, sex, email)values(?, ?, ?)`
r, err := tx.Exec(insert_cmd, "stu008", "man", "stu008@qq.com")
if err != nil {
fmt.Println("exec failed, ", err)
// Rollback
if rollbackErr := tx.Rollback(); rollbackErr != nil {
fmt.Println("update drivers: unable to rollback: %v", rollbackErr)
}
return
}
id, err := r.LastInsertId()
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("insert succ:", id) //insert succ: 3
//Commit
err = tx.Commit()
if err != nil {
fmt.Println("Failed to commit. Err: ", err)
return
}
//select
var person2 []Person
err = Db.Select(&person2, "select * from person")
if err != nil {
fmt.Println("exec failed, ", err)
return
}
fmt.Println("select succ:", person2) //select succ: [{2 stu002 femal stu01@qq.com} {3 Sara femal sara@qq.com}]
}

transactions

    注意:上面只是介绍了Go操作数据库的基本操作,更加详细的需要看: http://jmoiron.github.io/sqlx/

聊天系统设计:

修改聊天室程序,把数据存储部分切换到mysql中。

4. 参考文献

  • https://www.cnblogs.com/hanyouchun/p/6708037.html
  • https://www.2cto.com/database/201802/721140.html
  • https://blog.csdn.net/wdy_yx/article/details/78262528
  • http://jmoiron.github.io/sqlx/

Go语言学习之10 Web开发与Mysql数据库的更多相关文章

  1. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  2. 基于gin的golang web开发:mysql增删改查

    Go语言访问mysql数据库需要用到标准库database/sql和mysql的驱动.标准库的Api使用比较繁琐这里再引入另一个库github.com/jmoiron/sqlx. go get git ...

  3. android开发 如何通过web服务器访问MYSQL数据库并且使其数据同步到android SQLite数据库?

    通过web服务器访问MYSQL数据库有以下几个过程: 1.在MySql下创建自己的数据库和自己的表单 2.连接数据库. 3.访问数据库 1.创建web工程 (服务器端) 在Myeclipse下新建一个 ...

  4. 【MySQL】花10分钟阅读下MySQL数据库优化总结

    1.花10分钟阅读下MySQL数据库优化总结http://www.kuqin.com2.扩展阅读:数据库三范式http://www.cnblogs.com3.my.ini--->C:\Progr ...

  5. linux自动定时备份web程序和mysql数据库

    前些天受朋友说linux定时备份不知道怎么搞,叫帮忙处理一下.由于这段时间正闲着,所以也就欣然答应.由于朋友对linux不懂也希望我将操作的过程记录下来,也就是越详细越好.所以写得比较$%^& ...

  6. Java Web工程连接MySQL数据库及Tomcat服务器页面中文乱码

    Java Web工程连接MySQL数据库 一. 准备工作 1.下载连接MySQL数据库的JDBC (可以去官网下,也可以去百度云找) 2.将下载的jar文件复制到Tomcat的lib目录下 3.新建一 ...

  7. c语言学习基础:[1]开发工具介绍

    标签:c语言 1 2 3 4 分步阅读 学习编程语言的童鞋们一开始接触到的最多的估计就是C语言了,其次才是什么java.c++等,可以说学习c语言是我们走向编程世界的一座桥梁,学好它,对于我们学习和研 ...

  8. 【Python全栈-后端开发】MySQL数据库-练习题

    MySQL数据库-练习题 一.表关系 请创建如下表,并创建相关约束 二.操作表 1.自行创建测试数据 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号: 3.查询平均成绩大于60分的同学的学号 ...

  9. 10款免费的MySQL数据库图形化管理工具

    绝大多数的关系数据库都明显不同于MS Access,它们都有两个截然不同的部分:后端作为数据仓库,前端作为用于数据组件通信的用户界面.这种设计非常巧妙,它并行处理两层编程模型,将数据 层从用户界面中分 ...

随机推荐

  1. 20144306《网络对抗》MAL_恶意代码分析

    一.基础问题 1.如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控? 使用Windows自带的schtasks指 ...

  2. css3 伸缩布局 display:flex等

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 转载:Linux下解压zip乱码问题的解决(unzip)

    https://blog.csdn.net/abyjun/article/details/48344379 在windows上压缩的文件,是以系统默认编码中文来压缩文件.由于zip文件中没有声明其编码 ...

  4. java框架之SpringBoot(12)-消息及整合RabbitMQ

    前言 概述 大多数应用中,可通过消息服务中间件来提升系统异步通信.扩展解耦的能力. 消息服务中两个重要概念:消息代理(message broker)和目的地(destination).当消息发送者发送 ...

  5. 8 . IO类-标准IO、文件IO、stringIO

    8.1 IO类 #include <iostream> //标准IO头文件  8.2 文件输入输出流 #include <fstream> //读写文件头文件 std::fst ...

  6. 原生js阻止表单跳转

    /* W3C浏览器下的 */ var forms = document.getElementById("from") forms.addEventListener('submit' ...

  7. Cocos Creator 资源加载(笔记)

    cc.loader 加载资源动态加载资源要注意两点,一是所有需要通过脚本动态加载的资源,都必须放置在 resources 文件夹或它的子文件夹下.resources 需要在 assets 文件夹中手工 ...

  8. POJ 3080 Blue Jeans(Java暴力)

    Blue Jeans [题目链接]Blue Jeans [题目类型]Java暴力 &题意: 就是求k个长度为60的字符串的最长连续公共子串,2<=k<=10 规定: 1. 最长公共 ...

  9. c#中可变参数(params关键字的使用)

    一.params 是C#开发语言中关键字, params主要的用处是在给函数传参数的时候用,就是当函数的参数不固定的时候. 在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中 ...

  10. NFC读写电子便签总结

    编写NFC程序的基本步骤 1)设置权限,限制Android版本.安装的设备: 1 2 3 4 <uses-sdk android:minSdkVersion="14"/> ...