golang——database/sql包学习

本文介绍了Golang中database/sql包操作数据库的相关知识。涵盖数据库连接池管理、常用方法,如Open、Ping等;还介绍了数据表、数据行的操作,以及SQL命令状态和事务处理,包括事务的提交和回滚等内容。

1、database/sql包

sql包提供了保证SQL或类SQL数据库的泛用接口。

使用sql包时必须注入(至少)一个数据库驱动。

(1)获取mysql driver:go get -v github.com/go-sql-driver/mysql

(2)代码示例:

package main

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

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

// 检查错误
func checkErr(err error) {
	if err != nil {
		log.Fatalln(err)
	}
}

// 事务错误
func checkTxErr(err error, tx *sql.Tx) {
	if err != nil {
		log.Println(err)
		err = tx.Rollback()
		checkErr(err)
	}
}

// 数据库实体
type User struct {
	ID      int
	Name    string
	Age     int
	Sex     int
	AddDate time.Time
}

func main() {
	//1、获取数据库连接
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test?parseTime=true")
	checkErr(err)
	defer db.Close()
	fmt.Println("数据库连接成功")
	//2、判断连接是否有效
	err = db.Ping()
	checkErr(err)
	fmt.Println("数据库连接有效")
	//3、创建表
	sql := `
		CREATE TABLE IF NOT EXISTS users(
			id INT NOT NULL AUTO_INCREMENT,
			name VARCHAR(100) NOT NULL,
			age INT NOT NULL,
			sex TINYINT,
			add_date DATETIME,
			PRIMARY KEY(id) 
		)
	`
	_, err = db.Exec(sql)
	checkErr(err)

	//4、添加数据
	// sql = "INSERT INTO users (name,age,sex,add_date) VALUES (?,?,?,?)"
	// res, err := db.Exec(sql, "张三", 18, 1, time.Now())
	// checkErr(err)
	// fmt.Println(res.LastInsertId())

	//5、查询数据
	sql = "SELECT id,name,age,sex,add_date FROM users"
	rows, err := db.Query(sql)
	checkErr(err)
	defer rows.Close()
	user := User{}
	for rows.Next() {
		err = rows.Scan(&user.ID, &user.Name, &user.Age, &user.Sex, &user.AddDate)
		checkErr(err)
		fmt.Println(user, user.AddDate.Format("2006/01/02 15:04:05"))
	}
	err = rows.Err()
	checkErr(err)

	//6、查询一行
	sql = "SELECT id,name,age,sex,add_date FROM users"
	row := db.QueryRow(sql)
	err = row.Scan(&user.ID, &user.Name, &user.Age, &user.Sex, &user.AddDate)
	checkErr(err)
	fmt.Println(user)

	//7、命令
	sql = "UPDATE users SET name=? WHERE id=?;"
	stmt, err := db.Prepare(sql)
	checkErr(err)
	defer stmt.Close()
	result, err := stmt.Exec("李四", 1)
	checkErr(err)
	fmt.Println(result.RowsAffected())

	//8、查询
	sql = "SELECT id,name,age,sex,add_date FROM users WHERE id=?"
	stmt2, err := db.Prepare(sql)
	checkErr(err)
	defer stmt2.Close()
	row = stmt2.QueryRow(1)
	err = row.Scan(&user.ID, &user.Name, &user.Age, &user.Sex, &user.AddDate)
	checkErr(err)
	fmt.Println(user)

	//9、事务
	tx, err := db.Begin()
	checkErr(err)
	_, err = tx.Exec("UPDATE users SET age=? WHERE id=?", 20, 1)
	checkTxErr(err, tx)
	_, err = tx.Exec("UPDATE users SET sex=? WHERE id=?", 1, 1)
	checkTxErr(err, tx)
	err = tx.Commit()
	checkTxErr(err, tx)

	//10、查询一行
	sql = "SELECT id,name,age,sex,add_date FROM users"
	row = db.QueryRow(sql)
	err = row.Scan(&user.ID, &user.Name, &user.Age, &user.Sex, &user.AddDate)
	checkErr(err)
	fmt.Println(user)

}

2、数据库

2.1、type DB struct{}

DB是一个数据库句柄,代表一个具有零到多个底层连接的连接池。

它可以安全的被多个go程同时使用。

连接池的大小可以用SetMaxIdleConns方法控制。

2.2、常用方法

(1)func Open(driverName, dataSourceName string) (*DB, error)

打开数据库,返回数据库句柄,DB可以安全的被多个go程同时使用,并会维护自身的闲置连接池。

Open函数只需调用一次,很少需要关闭DB。

(2)func (db *DB) Driver() driver.Driver

返回数据库下层驱动。

(3)func (db *DB) Ping() error

检查与数据库的连接是否仍有效,如果需要会创建连接。

(4)func (db *DB) Close() error

关闭数据库,释放任何打开的资源。

一般不会关闭DB,因为DB句柄通常被多个go程共享,并长期活跃。

(5)func (db *DB) SetMaxOpenConns(n int)

设置与数据库建立连接的最大数目。

如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。

如果n <= 0,不会限制最大开启连接数,默认为0(无限制)。

(6)func (db *DB) SetMaxIdleConns(n int)

设置连接池中的最大闲置连接数。

如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。

如果n <= 0,不会保留闲置连接。

(7)func (db *DB) Exec(query string, args ...interface{}) (Result, error)

执行一次命令(包括查询、删除、更新、插入等),不返回任何执行结果。

参数args表示query中的占位参数。

(8)func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

执行一次查询,返回多行结果(即Rows),一般用于执行select命令。

(9)func (db *DB) QueryRow(query string, args ...interface{}) *Row

执行一次查询,并期望返回最多一行结果(即Row)。

总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。

(10)func (db *DB) Prepare(query string) (*Stmt, error)

创建一个准备好的状态用于之后的查询和命令。

返回值可以同时执行多个查询和命令。

(11)func (db *DB) Begin() (*Tx, error)

开始一个事务。

隔离水平由数据库驱动决定。

3、数据表

3.1、type Rows{}

Rows是查询的结果。

它的游标指向结果集的第零行,使用Next方法来遍历各行结果。

3.2、常用方法

(1)func (rs *Rows) Columns() ([]string, error)

返回列名。

如果Rows已经关闭会返回错误。

(2)func (rs *Rows) Scan(dest ...interface{}) error

Scan将当前行各列结果填充进dest指定的各个值中。

如果某个参数的类型为*[]byte,Scan会保存对应数据的拷贝,该拷贝为调用者所有,可以安全的,修改或无限期的保存。

如果参数类型为*RawBytes可以避免拷贝;参见RawBytes的文档获取其使用的约束。

如果某个参数的类型为*interface{},Scan会不做转换的拷贝底层驱动提供的值。

如果值的类型为[]byte,会进行数据的拷贝,调用者可以安全使用该值。

(3)func (rs *Rows) Next() bool

Next准备用于Scan方法的下一行结果。

如果成功会返回真,如果没有下一行或者出现错误会返回假。

每一次调用Scan方法,甚至包括第一次调用该方法,都必须在前面先调用Next方法。

(4)func (rs *Rows) Close() error

关闭Rows,阻止对其更多的列举。

如果Next方法返回假,Rows会自动关闭。

检查Err方法结果的条件。

Close方法是幂等的(多次调用无效的成功),不影响Err方法的结果。

(5)func (rs *Rows) Err() error

Err返回可能的、在迭代时出现的错误。

Err需在显式或隐式调用Close方法后调用。

4、数据行

4.1、type Row{}

QueryRow方法返回Row,代表单行查询结果。

4.2、常用方法

(1)func (r *Row) Scan(dest ...interface{}) error

Scan将该行查询结果各列分别保存进dest参数指定的值中。

如果该查询匹配多行,Scan会使用第一行结果并丢弃其余各行。

如果没有匹配查询的行,Scan会返回ErrNoRows。

5、SQL命令状态

5.1、type Stmt struct{}

Stmt是准备好的状态。

Stmt可以安全的被多个go程同时使用。

5.2、常用方法

(1)func (s *Stmt) Exec(args ...interface{}) (Result, error)

使用提供的参数执行准备好的命令状态,返回Result类型的该状态执行结果的总结。

(2)func (s *Stmt) Query(args ...interface{}) (*Rows, error)

使用提供的参数执行准备好的查询状态,返回Rows类型查询结果。

(3)func (s *Stmt) QueryRow(args ...interface{}) *Row

使用提供的参数执行准备好的查询状态。

如果在执行时遇到了错误,该错误会被延迟,直到返回值的Scan方法被调用时才释放。

返回值总是非nil的。

如果没有查询到结果,*Row类型返回值的Scan方法会返回ErrNoRows;否则,Scan方法会扫描结果第一行并丢弃其余行。

(4)func (s *Stmt) Close() error

关闭状态。

6、事务

6.1、type Tx struct{}

Tx代表一个进行中的数据库事务。

一次事务必须以对Commit或Rollback的调用结束。

调用Commit或Rollback后,所有对事务的操作都会失败并返回错误值ErrTxDone。

6.2、常用方法

(1)func (tx *Tx) Exec(query string, args ...interface{}) (Result, error)

执行命令,但不返回结果。例如执行insert和update。

(2)func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error)

执行查询并返回零到多行结果(Rows),一般执行select命令。

(3)func (tx *Tx) QueryRow(query string, args ...interface{}) *Row

执行查询并期望返回最多一行结果(Row)。

总是返回非nil的结果,查询失败的错误会延迟到在调用该结果的Scan方法时释放。

(4)func (tx *Tx) Prepare(query string) (*Stmt, error)

准备一个专用于该事务的状态。

返回的该事务专属状态操作在Tx递交会回滚后不能再使用。

(5)func (tx *Tx) Stmt(stmt *Stmt) *Stmt

使用已存在的状态生成一个该事务特定的状态。

(6)func (tx *Tx) Commit() error

提交事务。

(7)func (tx *Tx) Rollback() error

回滚事务。

转载于:https://www.cnblogs.com/dzhy/p/11065300.html

### Golang 处理复杂 SQL 查询的构建与执行 在 Go 语言中,处理复杂的 SQL 查询通常涉及以下几个方面:读取并解析 SQL 文件、动态构建查询语句以及安全地执行这些查询。以下是具体的实现方法: #### 1. 读取并解析 SQL 文件 可以使用标准库 `os` 和 `bufio` 来读取 SQL 文件的内容,并将其分割成单独的 SQL 语句。这一步骤对于批量执行多个 SQL 语句尤为重要。 ```go package main import ( "bufio" "fmt" "os" "strings" ) func readAndParseSQLFile(filePath string) ([]string, error) { file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() var sqlStatements []string scanner := bufio.NewScanner(file) buffer := new(strings.Builder) for scanner.Scan() { line := scanner.Text() if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "--") { // 忽略空行和注释 continue } buffer.WriteString(line + "\n") if strings.HasSuffix(line, ";") { // 判断是否为完整的 SQL 语句 sqlStatements = append(sqlStatements, buffer.String()) buffer.Reset() } } if err := scanner.Err(); err != nil { return nil, err } return sqlStatements, nil } ``` 此代码片段展示了如何逐行读取 SQL 文件并将每条语句存储到切片中[^1]。 --- #### 2. 动态构建 SQL 查询 当需要根据不同的条件动态生成 SQL 查询时,推荐使用字符串模板或者参数化查询的方式。这种方式不仅可以提高灵活性,还能有效防止 SQL 注入攻击。 ```go package main import ( "database/sql" "fmt" "log" _ "github.com/go-sql-driver/mysql" ) func buildDynamicQuery(name string, age int) (string, []interface{}) { query := "SELECT * FROM users WHERE 1=1" args := make([]interface{}, 0) if name != "" { query += " AND name LIKE ?" args = append(args, "%"+name+"%") } if age > 0 { query += " AND age >= ?" args = append(args, age) } return query, args } func executeQuery(db *sql.DB, query string, args []interface{}) { rows, err := db.Query(query, args...) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var username string err := rows.Scan(&id, &username) if err != nil { log.Fatal(err) } fmt.Printf("ID: %d, Name: %s\n", id, username) } } ``` 上述代码演示了如何基于输入参数动态拼接 SQL 查询,并通过绑定参数来增强安全性。 --- #### 3. 使用 ANTLR 解析复杂 SQL 查询 如果需要更深入地分析 SQL 的语法结构(例如提取 `WHERE` 子句中的条件),可以借助工具如 ANTLR。ANTLR 是一种强大的解析器生成器,能够帮助开发者轻松创建自定义的语言处理器。 以下是一个简单的例子,展示如何利用 Go 和 ANTLR 对 SQL 进行解析: ```bash antlr4 Sql.g4 -Dlanguage=Go go mod tidy go run . ``` 具体来说,可以通过定义 Grammar 文件(如 `Sql.g4`)描述 SQL 的语法规则,然后让 ANTLR 自动生成相应的解析器代码。之后,在程序中调用该解析器即可完成对 SQL 字符串的分解[^2]。 --- #### 4. 安全处理 NULL 值 在实际应用中,经常会遇到数据库字段可能为空的情况。此时可采用 `sql.NullString` 或其他类似的类型来进行适配。 ```go type User struct { Name sql.NullString Age sql.Valid[int] } func fetchUser(db *sql.DB, userID int) (*User, error) { row := db.QueryRow("SELECT name, age FROM users WHERE id=?", userID) user := &User{} err := row.Scan(&user.Name, &user.Age) if err != nil && err != sql.ErrNoRows { return nil, err } return user, nil } ``` 这里引入了 `sql.Valid[T]` 泛型支持(适用于较新的 Go 版本)。它允许更加灵活地表示任意类型的潜在缺失值[^3]。 --- ### 总结 以上介绍了几种常见的技术手段用于解决 Golang 下复杂 SQL 操作的需求——从基础的数据准备阶段直到高级别的逻辑控制层面都有所涉猎。无论是简单还是繁杂的任务场景都能找到合适的应对策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值