前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Golang】快速复习指南QuickReview(十一)——数据库访问(MySql为例)

【Golang】快速复习指南QuickReview(十一)——数据库访问(MySql为例)

作者头像
DDGarfield
发布2022-06-23 19:14:36
6450
发布2022-06-23 19:14:36
举报
文章被收录于专栏:加菲的博客加菲的博客

对于业务层面的开发,大多数都离不开数据库的访问。

1.创建项目

俗话说卖钱不卖钱,摊摊儿要扯圆,甭管怎样,我们先建立一个标准的golang项目,来访问数据库。

1.1 go mod 管理依赖

代码语言:javascript
复制
go mod init gitee.com/RandyField/sqltest

1.2 安装mysql驱动包

代码语言:javascript
复制
go get -u github.com/go-sql-driver/mysql

1.3 创建文件

代码语言:javascript
复制
cd sqltest
New-Item main.go  
New-Item service.go #数据库访问方法
New-Item models.go  #数据映射结构

2.连接数据库

main.go

代码语言:javascript
复制
package main

import (
 "context"
 "database/sql"
 "fmt"
 "log"

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

const (
 user      = "root"
 password  = "111111"
 database  = "push-center"
 protocol  = "tcp"
 address   = "127.0.0.1"
 charset   = "utf8mb4"
 parseTime = "True"
)

var db *sql.DB

func main() {
 /*
  https://pkg.go.dev/github.com/go-sql-driver/mysql
  https://github.com/denisenkom/go-mssqldb
  gorm-sqlx
 */
 // Connstr := fmt.Sprintf("%s:%s@%s(%s)/%s?charset=%s&parseTime=%s",
 //  user, password, protocol, address, database, charset, parseTime)
 // fmt.Println(Connstr)
 // Connstr := "root:111111@tcp(127.0.0.1)/push-center?charset=utf8mb4&parseTime=True"

 //parseTime 是查询结果是否自动解析为时间
 //loc 是MySQL的时区设置
 Connstr := "root:111111@tcp(127.0.0.1)/push-center?charset=utf8mb4&parseTime=True&loc=Local"
 fmt.Println("start...")
 var err error
 db, err = sql.Open("mysql", Connstr)
 if err != nil {
  log.Fatalln(err.Error())
 }

 ctx := context.Background()
 //验证连接是否有效
 err = db.PingContext(ctx)
 if err != nil {
  log.Fatalf(err.Error())
 }
 fmt.Println("Connected")
}
  • import "database/sql"
    • Golangdatabase/sql包提供了保证SQL或类SQL数据库的泛用接口。我们的数据库操作(编码)也只在database/sql包上进行。
  • import _ "github.com/go-sql-driver/mysql"
    • 连接数据库,需要加载目标数据库的驱动,Golang是没有提供官方的数据库驱动,所有的数据库驱动都是第三方驱动,但是它们都遵循sql.driver包里面定义的接口
    • 我们并不直接使用这个驱动,所以使用_引入,只需要在引入驱动包时调用包内init函数进行自动注册。

实际上,需要使用sql包的Register()

  • 数据库驱动名称
  • 并实现driver.Driver()接口的struct
  • 注册:sql.Register("mysql",&drv{})
  • Connstr:username/password@tcp(ipaddress)/database?parm1=&parm2=
    • parseTime是查询结果是否自动解析为时间
    • loc是MySQL的时区设置
    • 连接字符串,尤其注意后面的参数,博主在这里使用.netEFCore插入时间,值总是正确,而在使用golang时却总是有问题(晚8小时),无论在代码层面做何种转换。
  • sql.Open():仅仅是配置连接,但并不真正连接,需要两个参数:
    • 数据库驱动名称
    • 数据库连接字符串
    • 返回一个执行sql.DB这个struct的指针:*sql.DB

这个指针才是我们操作数据库的关键钥匙,我们编码通过这个指针发送sql命令,获得结果。它抽象了底层数据库连接池并对其维护,且并发安全,这便意味着我们可以在多个goroutine中并发使用。针对*sql.DB有两种用法:

  • 定义全局变量,然后到处使用
  • 定义变量,将其作为参数传递给函数或者方法
  • ctx := context.Background():Context(上下文)类型可以携带截止时间、取消信号和其他请求范围的值,并且可以横跨API边界和进程。但是这里的context包的Background()返回的Context很特殊,非nil的空Context,不会被取消也没有值,没有截止时间。
    • 通常用在main函数、初始化或测试中,作为传入请求的顶级Context
  • db.PingContext(ctx):验证与数据库的连接是否仍然有效,如有必要则建立一个连接。

3.访问数据库

访问之前我们需要能够映射数据库表的struct,但是struct非必需条件。

models.go

代码语言:javascript
复制
package main

//Notifypush 推送通知
type notifypush struct {
 Id          int    `json:"Id"`
 AppName     string `json:"APP"`
 Target      int    `json:"Platform"`
 TargetValue *string
 PushType    int
 DeviceType  int `json:"Device"`
 Title       *string
 Body        *string `json:"Content"`
 CreateTime  string
}

如果数据库字段有Null,可空类型, 结构体或者变量,都需要定义指针类型,否则会发生运行时错误。

3.1 查询单条

service.go

代码语言:javascript
复制
package main

import (
 "log"
 "time"
)

// GetById 根据ID获取单条数据
func GetById(id int) (notifypush, error) {
 n := notifypush{}
 err := db.QueryRow("select Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notifypush where Id=?",
  id).Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
 // err := db.QueryRow("select Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notitypush where Id=@Id",
 //  sql.Named("Id", id)).Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
 return n, err
}

如果是sqlserver,参数是使用@+参数名来进行站位,并配合sql.Named()函数使用。mysql不能这样,否则会报错mysql: driver does not support the use of Named Parameters

3.2 查询多条

代码语言:javascript
复制
// GetMultiRow 获取多条数据
func GetMultiRow(id int) (ns []notifypush, err error) {
 rows, err := db.Query("SELECT Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notifypush WHERE Id>?",
  id)
 // 非常重要:关闭rows释放持有的数据库链接
 defer rows.Close()

 for rows.Next() {
  n := notifypush{}
  err = rows.Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
  if err != nil {
   log.Fatalln(err.Error())
  }
  ns = append(ns, n)
 }
 return
}

3.2 修改、插入、删除

修改、插入、删除都是cmd,在sql包中只有一个方法:Exec,这里就省略delete操作,实际业务上很少使用物理删除。

Update

Update操作,需要定义方法(结构体为接收者)

代码语言:javascript
复制
//Update 更新
func (push *notifypush) Update() error {
 _, err := db.Exec("UPDATE notifypush SET AppName=?,Target=?,PushType=?,DeviceType=? WHERE Id=?",
  push.AppName, push.Target, push.PushType, push.DeviceType, push.Id)
 return err
}
Insert
代码语言:javascript
复制
//Insert 插入
func Insert() error {
 // _, err := db.Exec("INSERT INTO  notifypush(AppName,Target,PushType,DeviceType,CreateTime) VALUES(?,?,?,?,?)",
 //  "test-insert-app", 1, 2, 3, time.Now())

 // 改用 prepare
 sql := `INSERT INTO  notifypush(AppName,Target,PushType,DeviceType,CreateTime) VALUES(?,?,?,?,?)`
 stmt, err := db.Prepare(sql)
 if err != nil {
  log.Fatalln(err.Error())
 }
 defer stmt.Close()
 _, err = stmt.Exec("insert-app", 1, 2, 3, time.Now())
 return err
}

4.调用方法

4.1 编码

main.go

代码语言:javascript
复制
package main

import (
 "context"
 "database/sql"
 "fmt"
 "log"

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

const (
 user      = "root"
 password  = "111111"
 database  = "push-center"
 protocol  = "tcp"
 address   = "127.0.0.1"
 charset   = "utf8mb4"
 parseTime = "True"
)

var db *sql.DB

func main() {
 //ommit connect code
    
    //查询
    notify, err := GetById(10)
 if err != nil {
  log.Fatalf(err.Error())
 }
 data, err := json.Marshal(notify)
 fmt.Printf("推送消息为:%s\n", data)
    
    //更新
    notify.AppName = "smart"
 err = notify.Update()
 if err != nil {
  log.Fatalf(err.Error())
 }
    notify, err = GetById(10)
 if err != nil {
  log.Fatalf(err.Error())
 }
 data, err = json.Marshal(notify)
 fmt.Printf("更新后:%s\n", data)
    
    //插入
    err = Insert()
 if err != nil {
  log.Fatalf(err.Error())
 }
    
    //查询多条
    ns, err := GetMultiRow(24)
 datas, err := json.Marshal(ns)
 fmt.Printf("推送消息为:%s\n", datas)
}

4.2 运行

这里我们的项目是一个具有多个文件.gomodule,所以不能简单使用go run main.go。需要先编译,才能运行

代码语言:javascript
复制
go build #编译会生成sqltest.exe
.\sqltest.exe #运行

5.ORM

5.1 GORM

GORM是GoLang中最出色的ORM框架,支持MySQLPostgreSQLSqliteSQL Server,功能非常强大,也可以直接执行SQL并获取结果集。还有数据库迁移。博主把他看作Golang版本的EntityFramework

5.2 Sqlx

Sqlx是对GoLang标准database/sql的扩展。其特点是:

  1. 把SQL执行的结果集转化成数据结构(StructMapsSlices)。
  2. 支持问号(?)或命名的Prepared Statements,避免SQL注入的安全问题

在博主看来,这个更像是一个golang版本的dapper

参考连接

https://www.bilibili.com/video/BV1dZ4y1577v

https://qinzhiqiang.cn/2019/10/golang%E6%9C%8D%E5%8A%A1%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6-gorm-sqlx-mysql-mongodb/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 加菲的博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.创建项目
    • 1.1 go mod 管理依赖
      • 1.2 安装mysql驱动包
        • 1.3 创建文件
        • 2.连接数据库
        • 3.访问数据库
          • 3.1 查询单条
            • 3.2 查询多条
              • 3.2 修改、插入、删除
                • Update
                • Insert
            • 4.调用方法
              • 4.1 编码
                • 4.2 运行
                • 5.ORM
                  • 5.1 GORM
                    • 5.2 Sqlx
                    • 参考连接
                    相关产品与服务
                    云数据库 SQL Server
                    腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档