Go语言(Golang)数据库编程

这篇具有很好参考价值的文章主要介绍了Go语言(Golang)数据库编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Go 数据库编程

一、连接数据库

准备连接到数据库

  • 要想连接到 SQL 数据库,首先需要加载目标数据库的驱动,驱动里面包含着于该数据库交互的逻辑。
  • sql.Open()
    • 数据库驱动的名称
    • 数据源名称
    • 得到一个指向 sql.DB 这个 struct 的指针
  • sql.DB 是用来操作数据库的,它代表了0个或者多个底层连接的池,这些连接由sql 包来维护,sql 包会自动的创建和释放这些连接
    • 它对于多个 goroutine 并发的使用是安全的
package main

import (
  "context"
  "database/sql"
  "fmt"
  "log"
  
  _ "github.com/denisenkom/go-mssqldb"
)

var db *sql.DB

const (
  server = "xxxx.database.windows.net"
  port = 1433
  user = "xxxxx"
  password = "xxxxx"
  database = "go-db"
)

func main() {
  connStr := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;",
                        server, user, password, port, database)
  
  db, err := sql.Open("sqlserver", connStr)
  if err != nil {
    log.Fataln(err.Error())
  }
  
  ctx := context.Background()
  
  err = db.PingContext(ctx)
  if err != nil {
    log.Fataln(err.Error())
  }
  
  fmt.Println("Connected!")
}

Note

  • Open() 函数并不会连接数据库,甚至不会验证其参数。它只是把后续连接到数据库所必需的 structs 给设置好了
  • 而真正的连接是在被需要的时候才进行懒设置的
  • sql.DB 不需要进行关闭(当然你想关闭也是可以的)
  • 它就是用来处理数据库的,而不是实际的连接
  • 这个抽象包含了数据库连接的池,而且会对此进行维护
  • 在使用 sql.DB 的时候,可以定义它的全局变量进行使用,也可以将它传递到函数/方法里。

如何获得驱动

  • 正常的做法是使用 sql.Register() 函数、数据库驱动的名称和一个实现了 driver.Driver 接口的 struct,来注册数据库的驱动。例如:
    • sql.Register("sqlserver", &drv{})
  • 但是我们之前的例子却没写这句话,为什么?
    • 因为 Sql Server 的驱动,是在这个包被引入的时候进行了自我注册

驱动自动注册

  • 当 go-mssqldb 包被引入的时候,它的 init 函数将会运行并进行自我注册(在 Go 语言里,每个包的 init 函数都会在自动的调用)
  • 在引入 go-mssqldb 包的时候,把该包的名设置为下划线 _,这是因为我们不直接使用数据库驱动(只需要它的”副作用“),我们只使用 database/sql
    • 这样,如果未来升级驱动,也无需改变代码
  • Go 语言没有提供官方的数据库驱动,所有的数据库驱动都是第三方驱动,但是它们都遵循 sql.driver 包里面定义的接口

安装数据库驱动

  • 这是安装 Microsoft SQL Server 数据库驱动的例子:
  • go get github.com/denisenkom/go-mssqldb

func(*DB) PingContext

  • 上例中的 db.PingContext() 函数是用来验证与数据库的连接是否仍然有效,如有必要则建立一个连接。
  • 这个函数需要一个 Context (上下文)类型的参数,这种类型可以携带截止时间、取消信号和其它请求范围的值,并且可以横跨 API 边界和进程。
  • 上例中,创建 context 使用的是 context.Background() 函数。该函数返回一个非 nil 的空 Context。它不会被取消,它没有值,没有截止时间。
  • 它通常用在 main 函数、初始化或测试中,作为传入请求的顶级 Context。

Exercises

  • 使用 PostgreSQL 建立数据库,使用 Go 语言进行连接,并 Ping 一下。
  • 使用 SQLite 建立数据库,使用 Go 语言进行连接,并 Ping 一下。

连接MySQL

https://github.com/go-sql-driver/mysql

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

创建目录

➜ mcd go_sql_demo

Code/go/go_sql_demo via 🐹 v1.20.3 via 🅒 base
➜ go mod init go_sql_demo
go: creating new go.mod: module go_sql_demo

Code/go/go_sql_demo via 🐹 v1.20.3 via 🅒 base
➜ c

Code/go/go_sql_demo via 🐹 v1.20.3 via 🅒 base
➜

main.go

package main

import (
	"database/sql"
	"fmt"

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

// 定义一个全局对象db
var db *sql.DB

// 定义一个初始化数据库的函数
func initDB() (err error) {
	// DSN:Data Source Name
	dsn := "root:12345678@tcp(127.0.0.1:3306)/db_xuanke?charset=utf8mb4&parseTime=True"
	// 不会校验账号密码是否正确
	// 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		return err
	}
	return nil
}

func main() {
	err := initDB() // 调用输出化数据库的函数
	if err != nil {
		fmt.Printf("init db failed,err:%v\n", err)
		return
	}
	fmt.Println("connect to database")
}

二、CRUD

查询

  • sql.DB 类型上用于查询的方法有:
    • Query
    • QueryRow
    • QueryContext
    • QueryRowContext

Query

  • 返回的类型是:type Rows struct {}
  • Rows 的方法:
    • func (rs *Rows) Close() error
    • func (rs *Rows) ColumnTypes() ([]*ColumnType, error)
    • func (rs *Rows) Columns() ([]string, error)
    • func (rs *Rows) Err() error
    • func (rs *Rows) Next() bool
    • func (rs *Rows) NextResultSet() bool
    • func (rs *Rows) Scan(dest ...interface{}) error

QueryRow

  • 返回类型是:type Row struct {}
  • Row 的方法有:
    • func (r *Row) Err() error
    • func (r *Row) Scan(dest ...interface{}) error

https://pkg.go.dev/database/sql@go1.20.4

services.go 文件

package main

func getOne(id int) (a app, err error) {
  a = app{}
  log.Println(db == nil)
  err = db.QueryRow("SELECT Id, Name, Status, Level, [Order] FROM dbo.App WHERE Id=@Id", 				sql.Named("Id", id)).Scan(
    &a.ID, &a.name, &a.status, &a.level, &a.order)
  return
}

func getMany(id int) (apps []app, err error) {
  rows, err = db.Query("SELECT Id, Name, Status, Level, [Order] FROM dbo.App WHERE Id>@Id", 				sql.Named("Id", id))
  for rows.Next() {
    a := app{}
    err = rows.Scan(&a.ID, &a.name, &a.status, &a.level, &a.order)
    if err != nil {
      log.Fatalln(err.Error())
    }
    apps = append(apps, a)
  }
  return
}

models.go 文件

package main

type app struct {
  ID int
  name string
  status int
  level int
  order int
}

main.go 文件

package main

import (
  "context"
  "database/sql"
  "fmt"
  "log"
  
  _ "github.com/denisenkom/go-mssqldb"
)

var db *sql.DB

const (
  server = "xxxx.database.windows.net"
  port = 1433
  user = "xxxxx"
  password = "xxxxx"
  database = "go-db"
)

func main() {
  connStr := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;",
                        server, user, password, port, database)
  
  var err error
  db, err = sql.Open("sqlserver", connStr)
  if err != nil {
    log.Fataln(err.Error())
  }
  
  ctx := context.Background()
  
  err = db.PingContext(ctx)
  if err != nil {
    log.Fataln(err.Error())
  }
  
  fmt.Println("Connected!")
  
  log.Println(db == nil)
  // 查询一笔
  one, err := getOne(103)
  if err != nil {
    log.Fatal(err.Error())
  }
  fmt.Println(one)
  
  apps, err := getMany(103)
  if err != nil {
    log.Fatalln(err.Error())
  }
  
  fmt.Println(apps)
}

更新

  • sql.DB 类型上用于更新(执行命令)的方法有:
    • Exec
    • ExecContext

services.go 文件

func (a *app) Update() (err error) {
  _, err = db.Exec("UPDATE dbo.App SET Name=@Name, [Order]=@Order WHERE Id=@Id",
        sql.Named("Name", a.name), sql.Named("Order", a.order), sql.Named("Id", a.ID))
  if err != nil {
    log.Fatalln(err.Error())
  }
  return
}

main.go 文件

a, _ := getOne(103)
fmt.Println(a)
a.name += " 1234"
a.order++

err = a.Update()
if err != nil {
  log.Fatalln(err.Error())
}
a1, _ := getOne(103)
fmt.Println(a1)

删除

services.go 文件

func (a *app) Delete() (err error) {
  _, err = db.Exec("DELETE FROM dbo.App WHERE Id=@Id", sql.Named("Id", a.ID))
  if err != nil {
    log.Fatalln(err.Error())
  }
  return
}

其它

  • Ping
  • PingContext
  • Prepare
  • PrepareContext
  • Transactions
    • Begin
    • Begin Tx

services.go 文件

func (a *app) Insert() (err error) {
  statement := `INSERT INTO dbo.App
  (Name, NickName, Status, Level, [Order], Pinyin) 
  VALUES (@Name, 'Nick', &Status, @Level, @Order, '...');
  SELEÇT isNull(SCOPE_IDENTITY(), -1);`
  stmt, err := db.Prepare(statement)
  if err != nil {
    log.Fatalln(err.Error())
  }
  defer stmt.Close()
  err = stmt.QueryRow(
    sql.Named("Name", a.name), sql.Named("Status", a.status), 
    sql.Named("Level", a.level),
    sql.Named("Order", a.order)).Scan(&a.ID)
  
  if err != nil {
    log.Fatalln(err.Error())
  }
  
  return
}

main.go 文件

a := app {
  name: "Test",
  order: 1123,
  level: 10,
  status: 1,
}

err = a.Insert()
if err != nil {
  log.Fatalln(err.Error())
}

one, _ := getOne(a.ID)
fmt.Println(one)

三、MySQL CRUD 实践

在MySQL中创建一个名为sql_test的数据库

mysql> create database sql_test;
Query OK, 1 row affected (0.01 sec)

mysql> use sql_test;
Database changed
mysql> CREATE TABLE `user` (
    ->     `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    ->     `name` VARCHAR(20) DEFAULT '',
    ->     `age` INT(11) DEFAULT '0',
    ->     PRIMARY KEY(`id`)
    -> )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> show tables;
+--------------------+
| Tables_in_sql_test |
+--------------------+
| user               |
+--------------------+
1 row in set (0.01 sec)

mysql>

main.go 文件

package main

import (
	"database/sql"
	"fmt"

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

// 定义一个全局对象db
var db *sql.DB

// 定义一个初始化数据库的函数
func initDB() (err error) {
	// DSN:Data Source Name
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 不会校验账号密码是否正确
	// 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		return err
	}
	return nil
}

func main() {
	err := initDB() // 调用输出化数据库的函数
	if err != nil {
		fmt.Printf("init db failed,err:%v\n", err)
		return
	}
	fmt.Println("connect to database")

	// one, err := queryRowDemo(1)
	// if err != nil {
	// 	log.Fatal(err.Error())
	// }
	// fmt.Println(one)

	// u := user{
	// 	name: "小乔1",
	// 	age:  13,
	// 	id:   0,
	// }
	// err = u.insertRowDemo()
	// if err != nil {
	// 	log.Fatalln(err.Error())
	// }

	// u, _ := queryRowDemo(1)
	// fmt.Println("u:", u)
	// u.name = "貂蝉"
	// u.age = 16

	// err = u.updateRowDemo()
	// if err != nil {
	// 	log.Fatalln(err.Error())
	// }

	// u1, _ := queryRowDemo(1)
	// fmt.Println("u1:", u1)

	last_row, _ := queryMultiRowDemo(0)
	fmt.Println("last_row:", last_row)

	// var u = new(user)
	// u.id = 3
	// err = u.deleteRowDemo()
	// if err != nil {
	// 	log.Fatalln(err.Error())
	// }
}

services.go 文件

package main

import (
	"fmt"
)

// 查询单条数据示例
func queryRowDemo(id int) (u user, err error) {
	sqlStr := "select id, name, age from user where id=?"
	// 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放
	err = db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
	if err != nil {
		fmt.Printf("scan failed, err:%v\n", err)
		return
	}
	fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
	return
}

// 查询多条数据示例
func queryMultiRowDemo(id int) (u user, err error) {
	sqlStr := "select id, name, age from user where id > ?"
	rows, err := db.Query(sqlStr, id)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	// 非常重要:关闭rows释放持有的数据库链接
	defer rows.Close()
	// 循环读取结果集中的数据
	for rows.Next() {
		err = rows.Scan(&u.id, &u.name, &u.age)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
	}
	return
}

// 插入数据
func (u *user) insertRowDemo() (err error) {
	sqlStr := "insert into user(name, age) values (?,?)"
	ret, err := db.Exec(sqlStr, u.name, u.age)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	theID, err := ret.LastInsertId() // 新插入数据的id
	if err != nil {
		fmt.Printf("get lastinsert ID failed, err:%v\n", err)
		return
	}
	fmt.Printf("insert success, the id is %d.\n", theID)
	return
}

// 更新数据
func (u *user) updateRowDemo() (err error) {
	sqlStr := "update user set age=? where id = ?"
	ret, err := db.Exec(sqlStr, u.age, u.id)
	if err != nil {
		fmt.Printf("update failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操作影响的行数
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("update success, affected rows:%d\n", n)
	return
}

// 删除数据
func (u *user) deleteRowDemo() (err error) {
	sqlStr := "delete from user where id = ?"
	ret, err := db.Exec(sqlStr, u.id)
	if err != nil {
		fmt.Printf("delete failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操作影响的行数
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("delete success, affected rows:%d\n", n)
	return
}

models.go 文件

package main

type user struct {
	id   int
	age  int
	name string
}

运行文章来源地址https://www.toymoban.com/news/detail-438645.html

Code/go/go_sql_demo via 🐹 v1.20.3 via 🅒 base 
➜ go build . && ./go_sql_demo
connect to database
id:1 name:小乔 age:16
id:2 name:小乔 age:12
last_row: {2 12 小乔}

Code/go/go_sql_demo via 🐹 v1.20.3 via 🅒 base took 3.3s 
➜ 

到了这里,关于Go语言(Golang)数据库编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 什么是T-SQL编程?T-SQL是Transact-SQL的缩写,是一种扩展了SQL(结构化查询语言)的编程语言,用于Microsoft SQL Server数据库管理系统中的数据管理和操作。T-

    什么是T-SQL编程? T-SQL是Transact-SQL的缩写,是一种 扩展了SQL(结构化查询语言) 的编程语言,用于Microsoft SQL Server数据库管理系统中的数据管理和操作。T-SQL支持创建 存储过程、触发器、函数 等高级特性,能够更加灵活地进行数据操作和处理。基本的T-SQL语法与标准SQL很相

    2024年01月21日
    浏览(97)
  • 100天精通Golang(基础入门篇)——第15天:深入解析Go语言中函数的应用:从基础到进阶,助您精通函数编程!(进阶)

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月12日
    浏览(68)
  • Go语言编程教程-基本数据类型

    了解布尔类型 了解整数类型 了解浮点数类型 了解复数类型 了解字符串类型 了解字符类型 类型 名称 取值范围 说明 bool 布尔类型 true 或 false 默认值为 false 类型 名称 取值范围 说明 uint8 8位无符号整型 0 ~ 255 uint16 16位无符号整型 0 ~ 65535 uint32 32位无符号整型 0 ~ 4294967295 uint6

    2024年02月04日
    浏览(169)
  • GO编程语言:简洁、高效、强大的开源编程语言

    在现代软件开发领域,随着应用复杂度的不断提升,开发人员对编程语言的需求也日益增长。GO编程语言,作为一种简洁、高效且具备强大并发能力的新型开源编程语言,逐渐成为了许多开发者的首选。本文将详细介绍GO语言在哪些项目开发中表现出色,以及为什么许多开发者

    2024年02月02日
    浏览(107)
  • 【云原生 | 37】Docker快速部署编程语言Golang

    🍁 博主简介 :         🏅云计算领域优质创作者         🏅新星计划第三季python赛道第一名         🏅阿里云ACE认证高级工程师         🏅阿里云开发者社区专家博主 ✒️ 博主微信 :15575411187 💊 交流社区 :小鹏linux(个人社区)欢迎您的加入! 目录

    2024年02月01日
    浏览(64)
  • Go语言网络编程(socket编程)http编程

    Web服务器的工作原理可以简单地归纳为 客户机通过TCP/IP协议建立到服务器的TCP连接 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档 服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“

    2024年02月09日
    浏览(70)
  • Go语言网络编程(socket编程)WebSocket编程

    WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据 在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输 需要安装第

    2024年02月09日
    浏览(77)
  • Golang vs Java: 一场编程语言的较量

    在IT行业飞速发展的过程中,编程语言扮演着至关重要的角色。作为开发人员,选择合适的编程语言对于构建高效、可靠和可维护的应用程序至关重要。在这场编程语言的较量中,Golang和Java无疑是两个备受青睐的选择。我们现在将对这两种语言进行全面对比,探讨它们在性能、简洁

    2024年04月17日
    浏览(57)
  • GO语言网络编程(并发编程)Channel

    1.1.1 Channel 单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。 虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势

    2024年02月09日
    浏览(72)
  • GO语言网络编程(并发编程)select

    1.1.1 select多路复用 在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现: 这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,G

    2024年02月09日
    浏览(90)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包