go-mysql笔记
go-mysql笔记
安安一、安装配置 mysql 驱动
1.安装驱动
1 | go get -u github.com/go-sql-driver/mysql |
2.初始化模块
1 | go mod init name |
3.执行 go mod tidy
1 | go mod tidy |
4.导入驱动
1 | import( |
二、连接数据库
1.获取连接
1 | package main |
2.连接完整步骤
1 | package main |
3.深入理解 sql.Open
1 基本介绍
1 | func Open(driverName, dataSourceName string) (*sql.DB, error) |
参数说明:
- driverName:数据库驱动名称(如 “
mysql“、”postgres“、”sqlite3“) - dataSourceName:驱动特定的连接字符串(DSN)
基本用法:
1 | import ( |
2.核心特性
2.1 惰性连接机制
错误认知:
sql.Open() 立即建立数据库连接 ❌
实际行为:sql.Open() 仅初始化连接池,不建立实际连接 ✅
2.2 真正的连接时机
连接实际建立于:
- 首次执行查询或事务时(
Query(),Exec(),Begin()) - 显式调用
Ping()方法时
2.3 连接池管理
sql.Open() 返回的 *sql.DB 对象是 连接池管理器:
1 | // 连接池配置示例: |
3.连接验证
最佳实践:始终在 sql.Open() 后调用 Ping() 来验证连接
1 | // 正确使用示例 |
4.不同数据库的 DSN 格式
| 数据库 | DSN 格式示例 |
|---|---|
| MySQL | "user:password@tcp(localhost:3306)/dbname?params" |
| PostgreSQL | "postgres://user:password@localhost:5432/dbname?sslmode=disable" |
| SQLite | "file:/path/to/database.db?_fk=true" |
| SQL Server | "sqlserver://user:password@localhost:1433?database=dbname" |
5.生命周期管理
1 | func main() { |
6.注意事项
- 延迟连接:
sql.Open()不实际建立连接 - 必要验证:总是使用
Ping()验证连接 - 连接池管理:配置合理的连接池参数
- 生命周期:
- 应用启动时创建
*sql.DB - 应用退出时调用
Close() - 整个生命周期内复用
- 应用启动时创建
- 安全使用:
- 使用参数化查询防止注入
- 在事务中处理关键操作
- 及时释放
Rows和Tx资源
黄金法则:将 *sql.DB 视为长期存在的全局对象,而不是每次需要时创建和销毁的临时资源。
三、go-mysql查询操作
在 Go 语言中操作 MySQL 数据库进行查询,需要结合 database/sql 包和 MySQL 驱动(如 github.com/go-sql-driver/mysql)。下面是详细指南和最佳实践:
1. 基础查询操作
单行查询 (QueryRow)
1 | //待查询的表的结构 |
多行查询 (Query)
1 | //待查询的表的结构 |
2. 高级查询技巧
处理 NULL 值
1 | //表的结构 |
分页查询
1 | func getUsersPaginated(db *sql.DB, page, perPage int) ([]User, error) { |
关联查询(JOIN)
1 | //表的结构 |
3. 使用预编译语句 (Prepared Statements)
1 | func searchUsersByName(db *sql.DB, name string) ([]User, error) { |
4. 使用上下文 (Context)
1 | func getProductsWithTimeout(ctx context.Context, db *sql.DB) ([]Product, error) { |
5. 查询优化技巧
避免 SELECT *
1 | // 反例 ❌ |
使用缓冲提升性能
1 | func getLargeDataSet(db *sql.DB) ([]LargeItem, error) { |
使用 EXPLAIN 分析查询
1 | func explainQuery(db *sql.DB, query string, args ...interface{}) { |
6. 完整示例:带错误处理的查询
1 | func GetUserOrders(db *sql.DB, userID int) ([]Order, error) { |
关键注意事项
- 错误处理
- 总是检查
db.Query/db.QueryRow的错误 - 使用
sql.ErrNoRows特殊处理空结果 - 检查
rows.Err()捕获行遍历中的错误
- 总是检查
- 资源管理
- 使用
defer rows.Close()确保释放资源 - 在长期运行的函数中,提前关闭而不是依赖
defer
- 使用
- 参数化查询
- 总是使用
?占位符防止 SQL 注入 - 不要拼接 SQL 字符串
- 总是使用
- 性能优化
- 复用预编译语句
- 使用正确的数据类型减少转换开销
- 避免
SELECT *选择不必要的列
- 连接管理
- 使用
db.SetConnMaxIdleTime()防止数据库关闭空闲连接 - 在高并发应用中调整
SetMaxOpenConns和SetMaxIdleConns
- 使用
常见错误及解决
忘记关闭
rows1
2
3
4
5
6
7
8// 错误: 导致连接泄漏
rows, _ := db.Query(...)
// 忘记 rows.Close()
// 正确:
rows, err := db.Query(...)
if err != nil { ... }
defer rows.Close()扫描顺序不匹配
1
2
3
4
5// 错误: 列顺序不匹配
rows.Scan(&user.Name, &user.ID)
// 但查询是 SELECT id, name ...
// 正确: 保持查询列顺序和扫描顺序一致空结果处理不当
1
2
3
4
5
6
7// 错误: 没有处理 sql.ErrNoRows
err := db.QueryRow(...).Scan(...)
// 正确:
if errors.Is(err, sql.ErrNoRows) {
// 特殊处理空结果
}忽略上下文超时
1
2
3
4
5
6
7// 错误: 没有超时控制的慢查询
db.Query(...)
// 正确: 使用带有超时的Context
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
db.QueryContext(ctx, ...)
重:查询中用到的方法:
1.db.Query()
1.基本语法与功能
1 | rows, err := db.Query("SELECT ...", args...) |
- 返回结果集:返回一个
*sql.Rows对象,代表多行结果的集合 - 参数化查询:支持使用
?作为参数的占位符 - 高效资源管理:需要显式关闭
rows.Close()来释放数据库连接 - 查询非修改:仅用于查询操作(SELECT),不用于数据修改语句
2.基本用法案例
1 | rows, err := db.Query(` |
2.db.QueryRow()
db.QueryRow() 是 Go 的 database/sql 包中用于执行预期返回单行结果的 SQL 查询的关键方法。它简化了单行查询处理流程,并提供了比完整结果集更高效的资源利用方式。
1.核心特性与方法签名
1 | func (db *DB) QueryRow(query string, args ...interface{}) *Row |
- 专为单行结果设计:预期只返回一行数据
- 高效资源管理:自动关闭底层连接
- 简化错误处理:错误延后到
Scan()时处理 - 内置防错机制:若返回多行也只扫描第一行
2.基本用法
1 | func getUserByID(db *sql.DB, id int) (User, error) { |
替代方法与选择指南
| 方法 | 适用场景 | 特点 |
|---|---|---|
db.Query() |
多行查询 | 支持全功能结果集遍历 |
db.QueryRow() |
单行查询 | 简化代码,自动关闭资源 |
db.Exec() |
数据修改 (INSERT, UPDATE, DELETE) | 返回受影响行数,无结果集 |
db.QueryContext() |
需要上下文控制 | 支持超时和取消 |
tx.Query() |
事务中的查询 | 在事务上下文中运行 |
四、go-mysql插入数据
基础插入方法
1. 简单插入(Exec)
1 | func insertUser(db *sql.DB, name, email string) (int64, error) { |
2. 使用预处理语句(Prepared Statement)
1 | func prepareInsertProduct(db *sql.DB) func(Product) (int64, error) { |
高级插入技巧
1. 批量插入优化
方法1:拼接SQL语句(小批量)
1 | func batchInsertUsers(db *sql.DB, users []User) error { |
方法2:事务+预处理(大批量)
1 | func bulkInsertLogs(db *sql.DB, logs []Log) error { |
2. 插入特殊数据类型
1 | type Product struct { |
3. 使用 UPSERT 或 REPLACE
1 | // ON DUPLICATE KEY UPDATE |
使用上下文(Context)
1 | func insertWithTimeout(ctx context.Context, db *sql.DB, data Data) error { |
插入错误处理
1. 处理重复键错误
1 | func safeInsertUser(db *sql.DB, u User) error { |
2. 插入重试机制
1 | func insertWithRetry(db *sql.DB, query string, args ...interface{}) (int64, error) { |
插入性能优化
1. 批量插入参数优化
1 | func optimizedBulkInsert(db *sql.DB, items []Item) error { |
2. 使用 LOAD DATA INFILE(超大数据量)
1 | func bulkLoadCSV(db *sql.DB, csvPath string) error { |
完整示例:用户注册流程
1 | func registerUser(ctx context.Context, db *sql.DB, newUser User) (int64, error) { |
最佳实践总结
使用参数化查询
1
2
3
4
5// 错误 ❌ (SQL注入风险)
db.Exec(fmt.Sprintf("INSERT INTO users (name) VALUES ('%s')", userName))
// 正确 ✅
db.Exec("INSERT INTO users (name) VALUES (?)", userName)始终检查错误
1
2
3if _, err := db.Exec(...); err != nil {
// 处理错误
}复用预处理语句
1
2
3
4
5
6
7
8
9// 应用启动时
var insertUserStmt *sql.Stmt
func initDB() {
insertUserStmt = db.Prepare("INSERT ...")
}
// 应用中复用
insertUserStmt.Exec(...)重要操作使用事务
1
2
3tx.Begin()
// 多个操作
tx.Commit()处理NULL值
1
2
3
4type User struct {
Biography sql.NullString
Birthdate sql.NullTime
}批量插入优化
- 小批量:拼接SQL语句
- 大批量:事务+预处理
- 超大:LOAD DATA INFILE
超时控制
1
2
3ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
db.ExecContext(ctx, ...)连接池配置
1
2
3db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5*time.Minute)性能监控
1
2
3
4
5
6
7
8// 监控连接池状态
go func() {
for range time.Tick(1 * time.Minute) {
stats := db.Stats()
log.Printf("连接池: 打开=%d 使用中=%d 空闲=%d 等待=%d",
stats.OpenConnections, stats.InUse, stats.Idle, stats.WaitCount)
}
}()关闭资源
1
2
3
4
5// 确保关闭连接
defer db.Close()
// 及时关闭Rows和Statements
defer rows.Close()
defer stmt.Close()
五、go-mysql删除数据
基础删除操作
1. 简单删除(使用 Exec)
1 | func deleteUser(db *sql.DB, userID int64) error { |
2. 使用上下文(Context)控制
1 | func deleteWithTimeout(ctx context.Context, db *sql.DB, userID int64) error { |
高级删除场景
1. 事务中的删除(确保数据一致性)
1 | func deleteUserAndRelatedData(db *sql.DB, userID int64) error { |
2. 软删除(逻辑删除,使用标志位)
1 | func softDeleteProduct(db *sql.DB, productID int64) error { |
3. 批量删除
1 | func deleteExpiredSessions(db *sql.DB, maxAge time.Duration) (int64, error) { |
4. 联表删除(DELETE with JOIN)
1 | func deleteInactiveUsersWithNoOrders(db *sql.DB) (int64, error) { |
错误处理与安全措施
1. 处理外键约束错误
1 | func safeDeleteCategory(db *sql.DB, categoryID int64) error { |
2. 删除前验证条件(防止意外删除)
1 | func deleteUserWithVerification(db *sql.DB, userID int64, username string) error { |
3. 限流与重试机制
1 | func deleteWithRetry(db *sql.DB, query string, args ...interface{}) error { |
性能优化策略
1. 分批删除(避免锁表)
1 | func deleteLargeDataSet(db *sql.DB) error { |
2. 优化删除条件(使用索引)
1 | // 差 ❌ - 全表扫描 |
完整示例:安全删除服务
1 | package main |
最佳实践总结
始终使用参数化查询
1
2
3
4
5// 错误 ❌ (SQL注入风险)
db.Exec(fmt.Sprintf("DELETE FROM users WHERE id = %d", userID))
// 正确 ✅
db.Exec("DELETE FROM users WHERE id = ?", userID)使用事务保障数据完整性
1
2
3tx.Begin()
// 多个关联操作
tx.Commit()验证影响行数
1
2
3if rowsAffected, _ := result.RowsAffected(); rowsAffected == 0 {
return fmt.Errorf("目标记录不存在")
}实现软删除(逻辑删除)
1
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP NULL;
批量删除优化
1
2// 使用LIMIT分批删除
db.Exec("DELETE FROM large_table LIMIT 1000")上下文超时控制
1
2ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
db.ExecContext(ctx, ...)连接池配置
1
2
3db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5*time.Minute)错误处理与重试
1
2
3
4// 处理死锁等可重试错误
if isRetryableError(err) {
// 执行重试
}记录删除日志
1
2// 在事务中创建删除日志记录
tx.Exec("INSERT INTO deletion_logs ...")权限最小化
1
2
3-- 创建专用删除用户
CREATE USER 'deleter'@'localhost' IDENTIFIED BY 'strongpassword';
GRANT DELETE ON mydb.users TO 'deleter'@'localhost';
六、go-mysql更新数据
在 Go 语言中更新 MySQL 数据是常见的数据库操作。以下是从基础到高级的全面指南,涵盖各种更新场景和最佳实践。
基础更新操作
1. 简单更新(使用 Exec)
1 | func updateUserName(db *sql.DB, id int, newName string) error { |
2. 使用上下文(Context)控制超时
1 | func updateWithTimeout(ctx context.Context, db *sql.DB, id int, email string) error { |
高级更新技巧
1. 事务中的更新(确保数据一致性)
1 | func transferBalance(db *sql.DB, fromID, toID int, amount float64) error { |
2. 处理 NULL 值更新
1 | func updateUserProfile(db *sql.DB, id int, bio *string) error { |
3. 基于查询结果的更新(嵌套查询)
1 | func updateCustomerStatus(db *sql.DB) error { |
4. 批量更新
1 | func bulkUpdatePrices(db *sql.DB, productUpdates map[int]float64) error { |
5. 条件更新(乐观锁实现)
1 | func updateWithVersion(db *sql.DB, id int, newValue string) error { |
错误处理与重试
1. 处理死锁错误
1 | func updateUserStatsWithRetry(db *sql.DB, userID int) error { |
2. 处理唯一约束冲突
1 | func updateEmail(db *sql.DB, userID int, newEmail string) error { |
性能优化技巧
1. 高效批量更新(使用 CASE WHEN)
1 | func updateMultiProducts(db *sql.DB, updates map[int]float64) error { |
2. 更新后读取最新值(使用 OUTPUT 或 RETURNING)
1 | func updateAndReturn(db *sql.DB, id int, newName string) (User, error) { |
3. 避免全表扫描
1 | // 差 ❌ - 全表扫描 |
完整示例:库存管理系统
1 | package main |
最佳实践总结
始终使用参数化查询
1
2
3
4
5// 错误 ❌ (SQL注入风险)
db.Exec(fmt.Sprintf("UPDATE users SET name = '%s' WHERE id = %d", name, id))
// 正确 ✅
db.Exec("UPDATE users SET name = ? WHERE id = ?", name, id)验证影响行数
1
2
3if rowsAffected, _ := result.RowsAffected(); rowsAffected == 0 {
return fmt.Errorf("目标记录不存在")
}关键操作使用事务
1
2
3tx.Begin()
// 多个相关操作
tx.Commit()处理并发冲突
1
2
3
4
5-- 使用行级锁
SELECT ... FOR UPDATE
-- 使用版本号
UPDATE ... WHERE version = ?优化批量更新
- 小批量:使用
IN子句 - 大批量:使用
CASE WHEN或分批处理
- 小批量:使用
上下文超时控制
1
2ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
db.ExecContext(ctx, ...)连接池配置
1
2
3db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5*time.Minute)错误处理与重试
1
2
3
4// 处理死锁等可重试错误
if isRetryableError(err) {
// 执行重试
}避免全表更新
1
2-- 必须有WHERE条件
UPDATE products SET ... WHERE id = 123性能监控
1
2
3
4
5
6
7
8
9// 监控连接池状态
go func() {
ticker := time.NewTicker(1 * time.Minute)
for range ticker.C {
stats := service.db.Stats()
log.Printf("连接池: 打开=%d 使用中=%d 空闲=%d 等待=%d",
stats.OpenConnections, stats.InUse, stats.Idle, stats.WaitCount)
}
}()
七、Go 中 db.Exec() 方法
db.Exec() 是 Go 中 database/sql 包的核心方法,用于执行不返回行结果集的 SQL 命令。它对于执行插入(INSERT)、更新(UPDATE)、删除(DELETE)、DDL 语句等操作至关重要。
基本用法和语法
1 | result, err := db.Exec(query string, args ...interface{}) |
参数说明:
- query:要执行的 SQL 语句
- args:SQL 语句中的参数(用于参数化查询)
返回值:
- result:
sql.Result接口对象 - err:执行过程中遇到的错误
核心功能和方法
sql.Result 接口提供的方法:
1 | type Result interface { |
实际应用示例
1. 创建表(DDL 操作)
1 | // 创建用户表 |
2. 插入数据(INSERT)
1 | // 插入用户数据 |
3. 更新数据(UPDATE)
1 | // 更新用户邮箱 |
4. 删除数据(DELETE)
1 | // 删除非活动用户 |
5. 带参数的 DELETE 操作
1 | // 删除指定用户 |
高级用法与技巧
1. 使用事务处理多个操作
1 | // 转账操作事务示例 |
2. 使用预处理语句提高性能
1 | // 批量插入产品数据 |
3. 使用上下文控制超时
1 | func updateProfileWithTimeout(ctx context.Context, db *sql.DB, userID int, bio string) error { |
错误处理和常见问题
1. 处理唯一约束冲突
1 | _, err := db.Exec(` |
2. 处理外键约束错误
1 | _, err := db.Exec(` |
3. 处理结果未更新情况
1 | result, err := db.Exec(` |
性能优化建议
1. 批量操作优化
1 | // 高效批量更新方法 |
2. 正确配置连接池
1 | db, _ := sql.Open("mysql", dsn) |
3. 避免 N+1 更新问题
1 | // 差 ❌ - N+1 更新 |
安全最佳实践go
1. 参数化查询(防 SQL 注入)
1 | // 危险:SQL 注入风险 ❌ |
2. 最小权限原则
1 | -- 创建专用操作账号 |
3. 敏感操作审计
1 | // 在高危操作前记录日志 |
特殊场景处理
1. 更新 JSON 字段
1 | type ProductMetadata struct { |
2. 处理空值更新
1 | var email *string // 可能为 nil |
3. UPSERT 操作
1 | // MySQL 的 ON DUPLICATE KEY UPDATE |
完整示例:电商库存扣减服务
1 | package main |
总结:db.Exec() 最佳实践
始终使用参数化查询
1
2
3
4
5// 错误 ❌ (SQL注入风险)
db.Exec(fmt.Sprintf("DELETE FROM users WHERE id = %d", userID))
// 正确 ✅
db.Exec("DELETE FROM users WHERE id = ?", userID)关键操作使用事务
1
2
3
4tx, _ := db.Begin()
tx.Exec(...)
tx.Exec(...)
tx.Commit()检查操作结果
1
2
3if rowsAffected, _ := result.RowsAffected(); rowsAffected == 0 {
// 处理未更新情况
}使用上下文控制
1
2
3ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
db.ExecContext(ctx, ...)连接池优化
1
2
3db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5*time.Minute)批量操作处理
- 小批量:参数化语句
- 大批量:事务 + 预处理
错误分类处理
1
2
3
4
5
6if mysqlErr, ok := err.(*mysql.MySQLError); ok {
switch mysqlErr.Number {
case 1062: // 唯一约束
case 1451: // 外键约束
}
}资源及时关闭
1
2defer db.Close()
defer stmt.Close()使用预处理重用
1
2
3stmt, _ := db.Prepare("UPDATE ...")
defer stmt.Close()
stmt.Exec(...)日志审计
1
log.Printf("执行更新: %s", query)
八、go sql包中常用方法
database/sql 是 Go 标准库中用于数据库操作的核心包,提供了一套统一接口操作各种数据库。以下是该包中常用方法的全面总结和最佳实践指南。
核心接口与方法概览
1. 数据库连接管理
| 方法/函数 | 用途 | 示例 |
|---|---|---|
sql.Open |
初始化数据库连接 | db, err := sql.Open("mysql", dsn) |
db.Ping() |
验证连接是否有效 | err := db.Ping() |
db.Close() |
关闭连接并释放资源 | defer db.Close() |
sql.DB |
数据库连接池对象 | db.SetMaxOpenConns(25) |
2. 查询操作
| 方法 | 用途 | 返回类型 | 适用场景 |
|---|---|---|---|
db.Query() |
执行返回多行的查询 | *sql.Rows |
SELECT 返回多行结果 |
db.QueryRow() |
执行返回单行的查询 | *sql.Row |
SELECT 返回单行结果 |
db.QueryContext() |
带上下文的查询 | *sql.Rows |
需要超时或取消的查询 |
db.QueryRowContext() |
带上下文的单行查询 | *sql.Row |
需要超时或取消的单行查询 |
3. 非查询操作
| 方法 | 用途 | 返回类型 | 适用场景 |
|---|---|---|---|
db.Exec() |
执行不返回行的操作 | sql.Result |
INSERT, UPDATE, DELETE, DDL |
db.ExecContext() |
带上下文的非查询操作 | sql.Result |
需要超时或取消的写操作 |
sql.Result |
操作结果接口 | 接口 | 获取 LastInsertId, RowsAffected |
4. 预处理语句
| 方法 | 用途 | 返回类型 |
|---|---|---|
db.Prepare() |
创建预处理语句 | *sql.Stmt |
db.PrepareContext() |
带上下文的预处理 | *sql.Stmt |
stmt.Exec() |
执行预编译语句 | sql.Result |
stmt.Query() |
执行预编译查询 | *sql.Rows |
stmt.Close() |
关闭预处理语句 | - |
5. 事务处理
| 方法 | 用途 | 返回类型 |
|---|---|---|
db.Begin() |
开始事务 | *sql.Tx |
db.BeginTx() |
带选项开始事务 | *sql.Tx |
tx.Exec() |
在事务中执行操作 | sql.Result |
tx.Query() |
在事务中执行查询 | *sql.Rows |
tx.Commit() |
提交事务 | - |
tx.Rollback() |
回滚事务 | - |
6. 连接池管理
| 方法 | 用途 |
|---|---|
db.SetMaxOpenConns() |
设置最大打开连接数 |
db.SetMaxIdleConns() |
设置最大空闲连接数 |
db.SetConnMaxLifetime() |
设置连接最大生存时间 |
db.SetConnMaxIdleTime() |
设置连接最大空闲时间 |
db.Stats() |
获取连接池统计信息 |
Go database/sql 包中的常见类型详解
Go 的 database/sql 包提供了丰富的类型来处理数据库操作,理解这些核心类型对于编写高效、安全的数据库代码至关重要。以下是主要类型的详细解析:
核心结构类型
1. sql.DB - 数据库连接池
作用:
- 数据库操作的入口点
- 管理连接池(包括连接创建、重用和关闭)
主要方法
1 | // 执行不返回行的操作(INSERT, UPDATE, DELETE) |
连接池控制:
1 | // 连接池配置 |
2. sql.Tx - 事务处理器
作用:
- 表示数据库事务
- 提供在事务中执行操作的方法
核心方法:
1 | func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) |
示例用法:
1 | tx, err := db.BeginTx(ctx, &sql.TxOptions{ |
3. sql.Stmt - 预编译语句
作用:
- 预编译的 SQL 语句,可重复执行
- 提高执行效率和安全性
核心方法:
1 | func (s *Stmt) Exec(args ...interface{}) (Result, error) |
使用模式:
1 | // 全局准备预编译语句 |
4. sql.Rows - 多行结果集
作用:
- 表示 SELECT 查询返回的多行结果
- 支持迭代处理每一行数据
核心方法:
1 | func (rs *Rows) Close() error // 关闭结果集(必须调用) |
完整使用流程:
1 | rows, err := db.Query("SELECT id, name FROM products") |
5. sql.Row - 单行结果处理器
作用:
- 表示 SELECT 查询返回的单行结果
- 自动处理资源释放(无需 Close)
核心方法:
1 | func (r *Row) Scan(dest ...interface{}) error |
使用模式:
1 | func getUserName(db *sql.DB, id int) (string, error) { |
6. sql.Result - 操作结果
作用:
- 表示 Exec 操作的结果(INSERT, UPDATE, DELETE)
- 提供受影响行数和最后插入 ID 信息
核心方法:
1 | type Result interface { |
示例:
1 | result, err := db.Exec(` |
数据处理类型
1. 可空类型(处理 NULL 值)
1 | // 允许为空的字符串 |
使用示例:
1 | type UserProfile struct { |
2. sql.ColumnType - 列元数据类型
作用:
- 提供查询结果的列元数据信息
主要方法:
1 | func (ci *ColumnType) DatabaseTypeName() string // 数据库类型名 |
使用示例:
1 | rows, err := db.Query("SELECT * FROM products") |
错误类型与常量
1. 特殊错误
1 | var ( |
错误处理模式:
1 | err := db.QueryRow("SELECT ...").Scan(...) |
2. 事务隔离级别常量
1 | type IsolationLevel int |
使用方式:
1 | tx, err := db.BeginTx(ctx, &sql.TxOptions{ |
接口类型
1. sql.Scanner - 自定义扫描接口
1 | type Scanner interface { |
实现示例:
1 | type JSONData struct { |
2. sql.Valuer - 自定义值转换接口
1 | type Valuer interface { |
实现示例:
1 | type BoolAsInt bool |
连接池统计结构
sql.DBStats - 连接池统计信息
1 | type DBStats struct { |
使用示例:
1 | // 定时获取连接池状态 |
实战类型使用模式
1. 安全查询和扫描模式
1 | func queryProducts(db *sql.DB, category string) ([]Product, error) { |
2. 高级事务处理模式
1 | func completeOrder(db *sql.DB, orderID int) error { |


