Go语言操作数据库及其常规操作的示例代码


Posted in Golang onApril 21, 2021

Go操作MySQL

安装: go get -u github.com/go-sql-driver/mysql

GO语言的操作数据库的驱动原生支持连接池, 并且是并发安全的 标准库没有具体的实现 只是列出了一些需要的第三方库实现的具体内容

//第一次连接MySQL成功
package main

import (
 "database/sql"
 _ "github.com/go-sql-driver/mysql"   // _想当于init()初始化
 "log"
)

func main() {
 // root 用户名 1qa2ws3ed是密码  后边的书ip:port  gouse 库名
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
 db, err := sql.Open("mysql", dsn)
 if err != nil {
  panic(err)
 }
 // ping是尝试连接MySQL数据库
 
 if err = db.Ping(); err != nil{
  panic(err)
 }
 log.Fatalln("Mysql数据库连接成功")

}

Go调用MySQL封装成函数

package main

import (
 "database/sql"
 "encoding/json"
 "fmt"
 _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func InitDB() (err error) {
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"

 db, err = sql.Open("mysql", dsn)
 CheckErr(err)

 err = db.Ping()
 CheckErr(err)
 fmt.Println("数据库连接成功...")
 // 设置数据库连接池最大连接数
 db.SetConnMaxLifetime(10)

 //设置最大闲置连接数
 db.SetMaxIdleConns(5)

 return
}

type data struct {
 Username string `json:"username"`
 Password string `json:"password"`
}


func main()  {
 err := InitDB()
 CheckErr(err)

 query, err := db.Query("select username, password from test")
 CheckErr(err)

 for query.Next(){
  line := data{}
  // 查询数据的时候必须要调用scan方法如果 没有 使用scan  连接通道一直保持连接 无法释放连接  
  _ = query.Scan(&line.Username, &line.Password)
  fmt.Println(line)
  dataDic := map[string]string{
   "username": line.Username,
   "password": line.Password,
  }
  marshal, _ := json.Marshal(dataDic)
  fmt.Println(string(marshal))
 }
}

func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

GO—MySQL的增删改查

package main

import (
 "database/sql"
 "encoding/json"
 "fmt"
 "time"

 _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

// InitDB 数据库连接初始化
func InitDB() (err error) {
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"

 db, err = sql.Open("mysql", dsn)
 CheckErr(err)

 err = db.Ping()
 CheckErr(err)
 fmt.Println("数据库连接成功...")
 // 设置数据库连接池最大连接数
 db.SetConnMaxLifetime(10)

 //设置最大闲置连接数
 db.SetMaxIdleConns(5)

 return

}

type data struct {
 Username string `json:"username"`
 Password string `json:"password"`
}

// SelectQuery 查询函数
func SelectQuery() {
 sqlStr := "select username, password from test where id > ?"
 query, err := db.Query(sqlStr, 1)
 CheckErr(err)
 defer query.Close()

 fmt.Printf("现在是北京时间 %s , 你今天进步了吗?\n", time.Now().Format("2006-01-02 15:04:05"))

 for query.Next() {
  line := data{}
  // 查询数据的时候必须要调用scan方法如果 没有 使用scan  连接通道一直保持连接 无法释放连接
  _ = query.Scan(&line.Username, &line.Password)
  //fmt.Println(line)
  dataDic := map[string]string{
   "username": line.Username,
   "password": line.Password,
  }
  marshal, _ := json.Marshal(dataDic)
  fmt.Printf("查询到的数据为 %s\n", string(marshal))
 }
}

// InsertQuery 插入数据
func InsertQuery() {
 // sql 语句
 sqlStr := `insert into test (username,password) values ("kuQi", "123qwe")`
 result, err := db.Exec(sqlStr)
 CheckErr(err)
 id, err := result.LastInsertId()
 CheckErr(err)
 fmt.Printf("插入成功数据的id为 %v", id)
}

// UpdateQuery 更新数据函数
func UpdateQuery(dataField string, user string) {
 sqlStr := `update test set password=? where username=?`
 result, err := db.Exec(sqlStr, dataField, user)
 CheckErr(err)
 rowsAffected, err := result.RowsAffected()
 CheckErr(err)
 fmt.Printf("被更新字段的id为%d\n", rowsAffected)

}

// DeleteQuery 删除
func DeleteQuery(id int) {
 sqlStr := `delete from test where id=?`
 result, err := db.Exec(sqlStr, id)
 CheckErr(err)
 rowsAffected, err := result.RowsAffected()
 CheckErr(err)
 if rowsAffected == 0 {
  fmt.Printf("没有匹配到要删除的id=%d数据", id)
  return
 }
 fmt.Printf("删除数据库的id为%d", id)

}

//CheckErr 异常捕获函数
func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

// main 主函数 所有函数的入口
func main() {
 err := InitDB()
 CheckErr(err)

 //InsertQuery()
 UpdateQuery("hahaGolang123", "kuQi")
 SelectQuery()
 DeleteQuery(5)
}

MySQL的预处理

什么是预处理?
普通SQL语句执行过程:
 1.客户端对SQL语句进行占位符的替换得到了完整的SQL语句
 2.客户端发送完整SQL语句到MySQL服务端
 3.MySQL服务端执行完整的SQL语句并将结果返回终端

预处理的执行过程
 1.先把SQL语句拆分成两部分,SQL语句部分和参数部分
 2.先把SQL语句部分发送给MySQL服务端进行SQL预处理
 3.然后参数部分发送给MySQL服务端,MySQL对SQL语句进行拼接
 4.MySQL服务端执行完整的SQL语句返回结果

为什么要进行预处理?
  1.为了优化MySQL服务器重复执行SQL的方法。可以执行服务器的性能,提前让服务器编译,一次编译多次执行,节省后续重复编译的成本
  2.并且避免SQL注入

Go实现MySQL预处理

// prepare方法现将SQL发送到MySQL服务端, 返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令  ; 命令也就是SQL语句
// PrepareInsert 预处理执行插入语句
func PrepareInsert() {

 defer wg.Done()
 sqlStr := `insert into test (username, password) values (?, ?)`
 // - 预处理 stmt 就是编译好的sql语句 之后直接传递参数即可
 stmt, err := db.Prepare(sqlStr)
 var u1 = uuid.Must(uuid.NewV4())
 CheckErr(err)
 defer stmt.Close()
 i := rand.Int()

 username := fmt.Sprintf("yonghuming%d", i)
 result, err := stmt.Exec(username, u1.String()[:10])
 CheckErr(err)
 rowsAffected, err := result.LastInsertId()
 CheckErr(err)
 fmt.Printf("成功插入id=%d条数据\n", rowsAffected)
}

Go语言实现MySQL实现事务操作

// go语言中使用一下三个方法实现MySQL中的事务操作, 开始事务
func (db *DB) Begin()(*Tx, error)

// 提交事务  相当与Python中的conn.commit()
func (tx *Tx) Commit() error   

// 回滚事务
func (tx *Tx) Rollback() error

package main

import (
 "database/sql"
 "fmt"

 _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

type data struct {
 Username string `json:"username"`
 Password string `json:"password"`
}

// InitDB 数据库连接初始化
func InitDB() (err error) {
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"

 db, err = sql.Open("mysql", dsn)
 CheckErr(err)

 err = db.Ping()
 CheckErr(err)
 fmt.Println("数据库连接成功...")
 // 设置数据库连接池最大连接数
 db.SetMaxOpenConns(100)

 //设置最大闲置连接数
 db.SetMaxIdleConns(5)

 return

}

//CheckErr 异常捕获函数
func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

// TranSaCtIon MySQL的事务操作
func TranSaCtIon() {
 // 开启事务
 tx, err := db.Begin()
 CheckErr(err)

 // 执行多个SQL操作
 sqlStr := `update test set id=id+100000 where password=?`
 result, err := tx.Exec(sqlStr, "07f70f7e-4")
 CheckErr(err)
 id, err := result.LastInsertId()
 if err != nil {
  // 语句回滚
  err := tx.Rollback()
  fmt.Println("事务回滚")
  CheckErr(err)

 }
 fmt.Printf("修改后的id为%d\n", id)

}

func main() {
 err := InitDB()
 CheckErr(err)
 TranSaCtIon()
}

sqlx使用

第三方库sqlx能够简化操作,提高开发效率

安装go get github.com/jmoiron/sqlx

package main

import (
 "fmt"

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

var db *sqlx.DB

// InitDB 数据库初始化
func InitDB() (err error) {
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
 db, err = sqlx.Connect("mysql", dsn)
 CheckErr(err)
 db.SetMaxOpenConns(50)
 db.SetMaxIdleConns(10)
 fmt.Println("goUse 数据库连接成功")
 return
}

//CheckErr 异常捕获函数
func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

func main() {
 err := InitDB()
 CheckErr(err)
}

sqlx相较于原生的sql库好处在于 查询的时候sql原生的需要next scan 回调获取结果

sqlx 查询只需要定义一个存储的变量 然后自动就会将查询的出来的值放入变量中

package main

import (
 "encoding/json"
 "fmt"

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

var db *sqlx.DB

type user struct {
 ID       int    `json:"id"`
 Username string `json:"username"`
 Password string `json:"password"`
}

// InitDB 数据库初始化
func InitDB() (err error) {
 dsn := "root:1qa2ws3ed@tcp(127.0.0.1:3306)/gouse"
 // Connect 就是连接的同时db.ping()一下
 db, err = sqlx.Connect("mysql", dsn)
 CheckErr(err)
 db.SetMaxOpenConns(50)
 db.SetMaxIdleConns(10)
 fmt.Println("goUse 数据库连接成功")
 return
}

// SelectDB 查询单条数据的方法
func SelectDB() {
 sqlStr := `select * from test where id=?`
 var data user
 _ = db.Get(&data, sqlStr, 990)
 //CheckErr(err)
 fmt.Printf("%#v\n", data)
 marshal, err := json.Marshal(data)
 CheckErr(err)
 fmt.Println(string(marshal))
}

// ManySelect 查询多条数据方法
func ManySelect() {
 sqlStr := `select * from test where id < ?`
 var dataList []user
 err := db.Select(&dataList, sqlStr, 1000)
 CheckErr(err)
 //fmt.Println(dataList)
 marshal, err := json.Marshal(dataList)
 CheckErr(err)
 fmt.Println(string(marshal))
}

//CheckErr 异常捕获函数
func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

func main() {
 err := InitDB()
 CheckErr(err)
 SelectDB()
 ManySelect()

}

Go操作Redis

安装go get -u github.com/go-redis/redis

package main

import (
 "fmt"

 "github.com/go-redis/redis"
)

var redisDB *redis.Client

// InitRedisDB redis数据库初始化
func InitRedisDB() (err error) {

 redisDB = redis.NewClient(&redis.Options{
  Addr:     "127.0.0.1:6379",
  Password: "",
  DB:       0,
 })
 _, err = redisDB.Ping(redisDB.Context()).Result()
 CheckErr(err)
 fmt.Println("redis 连接成功")
 return
}

//CheckErr 异常捕获函数
func CheckErr(err error) {
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
}

func main() {
 _ = InitRedisDB()
}
  • set(key, value):给数据库中名称为key的string赋予值value
  • get(key):返回数据库中名称为key的string的value
  • getset(key, value):给名称为key的string赋予上一次的value
  • mget(key1, key2,…, key N):返回库中多个string的value
  • setnx(key, value):添加string,名称为key,值为value
  • setex(key, time, value):向库中添加string,设定过期时间time
  • mset(key N, value N):批量设置多个string的值
  • msetnx(key N, value N):如果所有名称为key i的string都不存在
  • incr(key):名称为key的string增1操作
  • incrby(key, integer):名称为key的string增加integer
  • decr(key):名称为key的string减1操作
  • decrby(key, integer):名称为key的string减少integer
  • append(key, value):名称为key的string的值附加value
  • substr(key, start, end):返回名称为key的string的value的子串

NSQ分布式消息队列

NSQ是目前比较流行的一个分布式消息队列,下面主要是NSQ及GO语言如何操作NSQ

NSQ是GO语言编写的一个开源的实时分布式内存消息队列, 其性能十分优异, NSQ的优势有:

​ 1.NSQ提倡分布式和扩散的拓扑,没有单点故障,支持容错和高可用性,并提供可靠的消息交付保证

​ 2.NSQ支持横向扩展, 没有任何集中式代理

​ 3.NSQ易于配置和部署,并且内置了管理界面

安装go get -u github.com/nsqio/go-nsq

Context

在Go HTTP 包的server中,每一个请求都在对应着一个响应,请求处理函数通常会启动额外的goroutine用来访问后端的服务,比如数据库和rpc服务,用来处理一个请求的goroutine通常需要访问一些与请求特定的数据,比如终端的身份认证信息、验证相关的token、请求和截止时间。当一个请求被取消或超时时,所有用来处理该请求的goroutine都应该迅速退出,然后系统才能释放这些goroutine

如何优雅的结束goroutine释放资源

// 通道版本
package main

import (
 "fmt"
 "sync"
 "time"
)

var wg sync.WaitGroup

func worker(exitChan <-chan struct{}) {
 defer wg.Done()
Test:
 for {
  fmt.Println("worker")
  time.Sleep(time.Second)
  select {
  case <-exitChan:
   break Test
  default:
  }

 }

}

func main() {
 wg.Add(1)
 c := make(chan struct{})

 go worker(c)
 time.Sleep(10 * time.Second)
 c <- struct{}{}
 close(c)
 wg.Wait()
 fmt.Println("Over")

}
// Context版本
package main

import (
 "context"
 "fmt"
 "sync"
 "time"
)

var wg sync.WaitGroup

func worker(ctx context.Context) {
 defer wg.Done()
Test:
 for {
  fmt.Println("worker")
  time.Sleep(time.Second)
  select {
  case <-ctx.Done():
   break Test
  default:
  }

 }

}

func main() {
 wg.Add(1)
 ctx, cancelFunc := context.WithCancel(context.Background())

 go worker(ctx)
 time.Sleep(10 * time.Second)

 cancelFunc()
 wg.Wait()
 fmt.Println("Over")
}

如果goroutine开启了新的goroutine,只需要将ctx传入到新的goroutine中即可

Background() 和 TODO()

go内置两个函数: Background() 和TUDO(),这两个函数分别返回了一个实现了context接口的background和todo. 我们代码中最开始都是以这两个内置的上下文对象作为最顶层的partent context,衍生出更多的子上下文对象。

backgroud() 主要用于main函数,初始化以及代码测试,作为context这个树结构的最顶层context,也就是跟context。

todo(),他目前还不知道能干点啥?

使用context的注意事项

  • 推荐以参数显示传递context
  • 以context作为参数的函数方法,应该把context作为第一个参数
  • 给一个函数传递context的时候,不要nil,如果不知道传递什么,就使用context.TODO()
  • context是并发安全的,可以随意在多个goroutine中传递

log标准库

log包定义了Logger类型, 该类型提供了一些格式化输出的方法。本包也提供了一个预定义的标准logger,可以通过调用函数Print系列,fatal系列和panic系列来使用,比自行创建的logger对象更容易使用。

package main

import "log"

func main() {
 log.Println("这是第一条工作日志")

 v := "THIS is worker log"
 log.Printf("%#v\n", v)
 // Fatal将会值写入信息之后,执行exit(1)
 log.Fatal("之后写一万行代码 我也不执行了哦")

 // 可以通过log.Panic 引发异常 会将日志写入之后引发异常
 log.Panic("测试panic的日志")

}

flag选项(日志输出内容设置)

log标准库提供了如下的flag选项,他们是一系列定义好的常量。

const (
 Ldate = 1 << iota
  Ltime
  Lmicroseconds
  Llongfile
  Lshortfile
  LUTC
  LstdFlags = Ldate | Ltime
)



package main
import "log"
func main() {
    // 设置默认附加的内容 
  log.SetFlags(log.Llongfile | log.Ltime)
    // 设置日志前缀
  log.SetPrefix("[go_log] ")
  log.Println("测试日志")

}
output>>>
[go_log] 19:02:14 /Users/mac/GolandProjects/src/day02/go_log库/main.go:19: 测试日志

配置日志输出位置

setoutput函数用来设置logger的输出目的地,默认是标准错误输出

package main

import (
 "log"
 "os"
)

func main() {

 file, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
 if err != nil {
  log.Panic("文件打开失败")
 }
  // 设置了写入文件 日志内容就不会打印到终端了
 log.SetOutput(file)
 log.SetFlags(log.Llongfile | log.Ltime)
 log.SetPrefix("[go_log] ")
 log.Println("测试日志")

}

我们可以定义一个init初始化函数 将log全部配置好 这样更加标准化

第三方日志库logrus的使用

logrus是GO结构化的logger 与上边的logger标准库完全兼容

安装logrusgo get github.com/sirupsen/logrus

package main

import (
 log "github.com/sirupsen/logrus"
)

func main() {
 log.WithFields(log.Fields{
  "animals": "dog",
  "time":    log.FieldKeyTime,
 }).Info("这是啥")
}

日志级别

Trace、debug、info、warning、error、fatal、panic

log.Trace("跟踪?")
 log.Debug("Debug?")
 log.Info("信息")
 log.Warn("警告?")
 log.Error("Something failed but I'm not quitting.")
 // 记完日志后会调用os.Exit(1) 
 log.Fatal("Bye.")
 // 记完日志后会调用 panic() 
 log.Panic("I'm bailing.")

日志记录

package main

import (
 "os"
 "time"

 log "github.com/sirupsen/logrus"
)

func main() {
 file, err := os.OpenFile("logrustest.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
 if err != nil {
  log.Panicln(err)
 }
 log.SetOutput(file)
 for i := 0; i < 100; i++ {
  log.WithFields(log.Fields{
   "animals": "dog",
   "Countey": "China",
   "City":    "BeiJing",
  }).Info("这是啥")
  time.Sleep(time.Second)
 }

 log.Trace("跟踪?")
 log.Info("信息")
 log.Warn("警告?")
 // 设置日志级别, 会记录info以上级别(warn error fatal panic)
 log.SetLevel(log.InfoLevel)

}

>>>结果
time="2021-02-04T12:00:15+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:17+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:18+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog
time="2021-02-04T12:00:19+08:00" level=info msg="这是啥" City=BeiJing Countey=China animals=dog

日志的条目除了使用withfield 和withfields添加的相关日志,还有一些默认添加的日志字段

time 记录日志的时间戳 msg 记录日志信息 level记录日志级别

日志格式化

logrus内置一下两种日志格式化程序

logrus.TextFormatter logrus.JSONFormatter

log.SetFormatter(&log.JSONFormatter{})

追踪函数

log.SetReportCaller(true)

这样就会将哪个文件哪一行 都记录下来  但是不是特殊需求无需开启这个 因为会增加性能开

到此这篇关于Go语言操作数据库及其常规操作的示例代码的文章就介绍到这了,更多相关Go语言操作数据库内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
Go Gin实现文件上传下载的示例代码
Apr 02 Golang
Go语言操作数据库及其常规操作的示例代码
Apr 21 Golang
golang中的空slice案例
Apr 27 Golang
golang 实现Location跳转方式
May 02 Golang
Golang: 内建容器的用法
May 05 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
golang内置函数len的小技巧
Jul 25 Golang
深入理解go缓存库freecache的使用
Feb 15 Golang
如何解决goland,idea全局搜索快捷键失效问题
Apr 03 Golang
Golang 字符串的常见操作
Apr 19 Golang
go goth封装第三方认证库示例详解
Aug 14 Golang
Go gorilla securecookie库的安装使用详解
Aug 14 Golang
Go各时间字符串使用解析
Apr 02 #Golang
Go Gin实现文件上传下载的示例代码
Apr 02 #Golang
一文读懂go中semaphore(信号量)源码
Apr 03 #Golang
go语言map与string的相互转换的实现
Apr 07 #Golang
Golang二维切片初始化的实现
Apr 08 #Golang
为什么不建议在go项目中使用init()
Apr 12 #Golang
golang判断key是否在map中的代码
Apr 24 #Golang
You might like
探讨:如何使用PhpDocumentor生成文档
2013/06/25 PHP
php微信高级接口调用方法(自定义菜单接口、客服接口、二维码)
2016/11/28 PHP
php Session无效分析资料整理
2016/11/29 PHP
php获取开始与结束日期之间所有日期的方法
2016/11/29 PHP
使用PHP+Redis实现延迟任务,实现自动取消订单功能
2019/11/21 PHP
php使用redis的几种常见操作方式和用法示例
2020/02/20 PHP
javascript String 的扩展方法集合
2008/06/01 Javascript
javascript 禁止复制网页
2009/06/11 Javascript
js prototype截取字符串函数
2010/04/01 Javascript
表格单元格交错着色实现思路及代码
2013/04/01 Javascript
jquery实现的下拉和收缩效果示例
2014/08/21 Javascript
JavaScript中的object转换成number或string规则介绍
2014/12/31 Javascript
JQuery中ajax方法访问web服务实例
2015/07/18 Javascript
jquery UI Datepicker时间控件的使用方法(终结版)
2015/11/07 Javascript
jQuery代码实现对话框右上角菜单带关闭×
2016/05/03 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
2016/10/01 Javascript
Angular-Touch库用法示例
2016/12/22 Javascript
vue component组件使用方法详解
2017/07/14 Javascript
vue项目前端埋点的实现
2019/03/06 Javascript
VUE实现自身整体组件销毁的示例代码
2020/01/13 Javascript
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
Linux环境下MySQL-python安装过程分享
2015/02/02 Python
python抓取网页中图片并保存到本地
2015/12/01 Python
Python实现采用进度条实时显示处理进度的方法
2017/12/19 Python
Python求两个圆的交点坐标或三个圆的交点坐标方法
2018/11/07 Python
python傅里叶变换FFT绘制频谱图
2019/07/19 Python
Anaconda和ipython环境适配的实现
2020/04/22 Python
一款利用css3的鼠标经过动画显示详情特效的实例教程
2014/12/29 HTML / CSS
canvas实现飞机打怪兽射击小游戏的示例代码
2018/07/09 HTML / CSS
Sunglasses Shop英国:欧洲领先的太阳镜在线供应商之一
2018/09/19 全球购物
晚宴邀请函范文
2014/01/15 职场文书
优秀共产党员先进事迹
2014/01/27 职场文书
运动会通讯稿300字
2014/02/02 职场文书
《挑山工》的教学反思
2014/02/16 职场文书
2014年端午节演讲稿范文
2014/05/23 职场文书
HTML5 新增内容和 API详解
2021/11/17 HTML / CSS