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:垃圾回收GC触发条件详解
Apr 24 Golang
golang 如何用反射reflect操作结构体
Apr 28 Golang
Golang之sync.Pool使用详解
May 06 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
go使用Gin框架利用阿里云实现短信验证码功能
Aug 04 Golang
深入理解go slice结构
Sep 15 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
golang操作rocketmq的示例代码
Apr 06 Golang
Go语言的协程上下文的几个方法和用法
Apr 11 Golang
Golang jwt身份认证
Apr 20 Golang
Go gRPC进阶教程gRPC转换HTTP
Jun 16 Golang
基于Python实现西西成语接龙小助手
Aug 05 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
用PHP的ob_start() 控制您的浏览器cache
2009/08/03 PHP
php 目录遍历、删除 函数的使用介绍
2013/04/28 PHP
PHP简单实现无限级分类的方法
2016/05/13 PHP
通过JAVASCRIPT读取ASP设定的COOKIE
2006/11/24 Javascript
IE 条件注释详解总结(附实例代码)
2009/08/29 Javascript
ASP Json Parser修正版
2009/12/06 Javascript
jQuery UI Autocomplete 1.8.16 中文输入修正代码
2012/04/16 Javascript
深入理解JavaScript中的并行处理
2016/09/22 Javascript
几句话带你理解JS中的this、闭包、原型链
2016/09/26 Javascript
详解vue的数据binding绑定原理
2017/04/12 Javascript
微信小程序 Buffer缓冲区的详解
2017/07/06 Javascript
Typescript3.9 常用新特性一览(推荐)
2020/05/14 Javascript
vue-admin-template配置快捷导航的代码(标签导航栏)
2020/09/04 Javascript
Ajax获取node服务器数据的完整步骤
2020/09/20 Javascript
Python中对列表排序实例
2015/01/04 Python
教你用Type Hint提高Python程序开发效率
2016/08/08 Python
Python3正则匹配re.split,re.finditer及re.findall函数用法详解
2018/06/11 Python
Python3实现转换Image图片格式
2018/06/21 Python
对Python 内建函数和保留字详解
2018/10/15 Python
通过pykafka接收Kafka消息队列的方法
2018/12/27 Python
python如何将多个PDF进行合并
2019/08/13 Python
SELENIUM自动化模拟键盘快捷键操作实现解析
2019/10/28 Python
借助Paramiko通过Python实现linux远程登陆及sftp的操作
2020/03/16 Python
python使用建议与技巧分享(一)
2020/08/17 Python
Python制作运行进度条的实现效果(代码运行不无聊)
2021/02/24 Python
CSS3 Pie工具推荐--让IE6-8支持一些优秀的CSS3特性
2014/09/02 HTML / CSS
让IE下支持Html5的placeholder属性的插件
2014/09/02 HTML / CSS
微信端html5页面调用分享接口示例
2018/03/14 HTML / CSS
Baracuta官方网站:Harrington夹克,G9,G4,G10等
2018/03/06 全球购物
优秀求职信范文分享
2013/12/19 职场文书
生产工厂门卫岗位职责
2014/09/26 职场文书
2015小学教育教学工作总结
2015/07/21 职场文书
2015年科普工作总结
2015/07/23 职场文书
美德少年事迹材料(2016推荐版)
2016/02/25 职场文书
JavaScript原型链中函数和对象的理解
2022/06/16 Javascript
Python测试框架pytest核心库pluggy详解
2022/08/05 Golang