惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Threat Research - Cisco Blogs
NISL@THU
NISL@THU
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
J
Java Code Geeks
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
T
Tor Project blog
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
P
Privacy & Cybersecurity Law Blog
MongoDB | Blog
MongoDB | Blog
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
小众软件
小众软件
G
GRAHAM CLULEY
P
Privacy International News Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
人人都是产品经理
人人都是产品经理
S
Schneier on Security
Scott Helme
Scott Helme
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
E
Exploit-DB.com RSS Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
U
Unit 42
The Register - Security
The Register - Security
S
Securelist
Martin Fowler
Martin Fowler
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
Cisco Talos Blog
Cisco Talos Blog

博客园 - 自然洒脱

MiniMax 权益码 Token Plan 套餐 9 折优惠 go语言context包 go语言通道 go语言GMP模型 go语言mongodb操作 go语言gorm的CRUD go语言gorm go语言log相关 go语言包管理 go语言时间相关 go语言序列化和反序列化 go语言的"面向对象" go语言结构体排序 go语言异常处理 go语言接口 go语言结构体(二) go语言结构体(一) - 自然洒脱 go语言递归函数及defer go语言函数作用域及匿名函数
go语言mysql驱动
自然洒脱 · 2023-07-23 · via 博客园 - 自然洒脱

连接数据库是典型的CS编程,服务器端被动等待客户端建立TCP连接,并在此连接上进行特定的应用层 协议。但一般用户并不需要了解这些细节,这些都被打包到了驱动库当中,只需要简单的调用打开就可以指定协议连接到指定的数据库。

数据库的种类和产品太多,协议太多,Go官方很难提供针对不同数据库的驱动程序,往往由各数据库官 方或第三方给出不同开发语言的驱动库。但是,为了Go语言可以提前定义操作一个数据库的所有行为 (接口)和数据(结构体)的规范,这些定义在database/sql下。

MySQL驱动

  • https://github.com/go-sql-driver/mysql 支持 database/sql,使用广泛
  • https://github.com/ziutek/mymysql 支持 database/sql,支持自定义接口
  • https://github.com/Philio/GoMySQL 不支持 database/sql,支持自定义接口

注册驱动:

// github.com/go-sql-driver/mysql/mysql/driver.go 代码中有注册驱动
func init() { // 83行
 sql.Register("mysql", &MySQLDriver{})
}

连接

DSN例子 https://github.com/go-sql-driver/mysql#examples

 使用示例:

package logg

import (
    "io"

    "github.com/rs/zerolog"
)

func InfoLog(out io.Writer) zerolog.Logger {
    zerolog.TimeFieldFormat = "2006-01-02 15:04:05.000 +0800"
    logger := zerolog.New(out).With().Timestamp().Logger().Level(1)
    return logger
}

logg/logg.go

package main

import (
    "cabel/logg"
    "database/sql" // 接口定义库
    "fmt"
    "os"
    "time"

    _ "github.com/go-sql-driver/mysql" // 具体实现,可以不调用,加载一下就行,在init中注册mysql这个名字
    "github.com/rs/zerolog/log"
)

var db0 *sql.DB

func init() {
    connstr := "gopher:123456@tcp(172.0.0.1:3306)/gopher"
    var err error
    // 接口库的方法,指定使用的数据库种类,名称就要找到该名称对应的数据库的驱动
    db0, err = sql.Open("mysql", connstr)
    if err != nil {
        log.Err(err).Send()
    }
    db0.SetConnMaxIdleTime(time.Second * 30) // 连接超时时间
    // 数据库连接最大生命周期,用于指定数据库连接在被重用之前的最大存活时间
    db0.SetConnMaxLifetime(time.Second * 60)
    db0.SetMaxOpenConns(100) // 最大连接数,默认0为不限制
    db0.SetMaxIdleConns(10)  // 空闲连接数
}

// 定义结构体,结构体中字段与数据库中字段顺序最好一一对应
type Emp struct {
    emp_no                            int
    birth_date, first_name, last_name string
    gender                            int
    hire_date                         string
}

func main() {
    f0, err := os.OpenFile("logs/info.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Err(err).Send()
    }
    defer f0.Close()
    f1, err := os.OpenFile("logs/error.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Err(err).Caller().Send()
    }
    defer f1.Close()
    l0 := logg.InfoLog(f0)
    l1 := logg.InfoLog(f1)
    err = db0.Ping()
    if err == nil {
        l0.Info().Msg("数据库初始化成功")
    } else {
        l1.Err(err).Msg("数据库连接失败")
    }

    // 单行查询
    row := db0.QueryRow("select * from employees where emp_no > ? limit 1", 10007)
    if row.Err() != nil {
        l1.Err(row.Err()).Caller().Msg("数据库查询失败")
    }
    var r Emp
    row.Scan(&r.emp_no, &r.birth_date, &r.first_name,
        &r.last_name, &r.gender, &r.hire_date)
    fmt.Println(r)

    // 预编译查询,多行查询
    stmt, err := db0.Prepare("select * from employees where emp_no > ? and emp_no < ? order by emp_no limit 3")
    if err != nil {
        l1.Err(err).Caller().Msg("预编译查询语句失败")
    }
    rows, err := stmt.Query("10003", 10008)
    if err != nil {
        l1.Err(err).Caller().Msg("查询失败")
    }
    // Next来遍历,每一次,rows都指向当前行
    for rows.Next() {
        err = rows.Scan(&r.emp_no, &r.birth_date, &r.first_name,
            &r.last_name, &r.gender, &r.hire_date)
        if err != nil {
            l1.Err(err).Msg("填充失败")
        }
        fmt.Println(r)
        t0, err := time.Parse("2006-01-02", r.birth_date) // 时间解析
        if err != nil {
            l1.Err(err).Send()
        }
        fmt.Printf("%T %[1]v\n", t0)
    }
}

main.go

返回结果:

使用db.Prepare预编译并使用参数化查询

  • 对预编译的SQL语句进行缓存,省去了每次解析优化该SQL语句的过程
  • 防止注入攻击
  • 使用返回的sql.Stmt操作数据库