【Go语言基础】Go语言 & MySQL数据库 —— 数据操作:无上下文 Context
Satori2Core 2025/6/7 Go项目环境搭建
# Go原生MySQL操作指南
Go 语言操作 Mysql 的理解:
Go语言原生操作MySQL的核心就是通过 sql.DB 实例的 Exec() 等方法执行原始SQL语句
# 1. 核心方法使用场景对照表
操作类型 | 推荐方法 | 返回值 | 使用场景 |
---|---|---|---|
数据库DDL | Exec() | sql.Result | 创建/删除数据库 |
表结构DDL | Exec() | sql.Result | 建表/删表/列操作/索引操作 |
写操作 | Exec() | sql.Result | INSERT/UPDATE/DELETE |
单行查询 | QueryRow() | *sql.Row | 获取单条记录(如按ID查询) |
多行查询 | Query() | *sql.Rows | 获取多条记录(如列表查询) |
预处理语句 | Prepare() | *sql.Stmt | 高频重复SQL操作 |
事务操作 | Begin() | *sql.Tx | 多操作原子执行 |
# 2. 数据库操作(DDL)
- 核心方法:
Exec()
# 2.1 数据库:增/删/切换
// 创建数据库
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARSET utf8mb4")
// 删除数据库
_, err := db.Exec("DROP DATABASE IF EXISTS shop_old")
// 切换数据库
_, err := db.Exec("USE shop")
# 2.2 表操作:增/删/改/索引
此处简略演示,更多使用:可以看前文【Go 操作数据表】部分
// 创建表
_, err := db.Exec(`CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB`)
// 删除表
_, err := db.Exec("DROP TABLE IF EXISTS temp_products")
// 修改表结构
_, err := db.Exec("ALTER TABLE products ADD COLUMN stock INT DEFAULT 0")
// 索引操作
_, err := db.Exec("CREATE INDEX idx_product_name ON products (name)")
_, err := db.Exec("DROP INDEX idx_product_name ON products")
# 3. 数据操作(DML)
# 3.1 写操作(INSERT/UPDATE/DELETE)
- 核心方法:
Exec()
- 相关方法:
(sql.Result).LastInsertId()
:获取插入ID(sql.Result).RowsAffected()
:获取影响行数
// 插入数据(带参数)
result, err := db.Exec(
"INSERT INTO products (name, price) VALUES (?, ?)",
"Go语言教程", 88.88
)
// 获取插入ID
newID, _ := result.LastInsertId()
// 更新数据
result, err := db.Exec(
"UPDATE products SET price = ? WHERE id = ?",
99.99, 100
)
// 获取影响行数
rowsAffected, _ := result.RowsAffected()
// 删除数据
result, err := db.Exec("DELETE FROM products WHERE id = ?", 101)
# 3.2 读操作(SELECT)
- **核心方法:
QueryRow() / Query()
** QueryRow()
:获取单条记录(如按ID查询)Query()
:获取多条记录(如列表查询),- 注意:多行查询,必须关闭结果集(如下示例)
// 单行查询
var (
name string
price float64
)
err := db.QueryRow(`
SELECT name, price FROM products WHERE id = ?
`, 100).Scan(&name, &price)
// 多行查询
rows, err := db.Query(`
SELECT id, name, price FROM products WHERE price > ?
`, 50.0)
defer rows.Close() // 必须关闭结果集
for rows.Next() {
var (
id int
name string
price float64
)
if err := rows.Scan(&id, &name, &price); err != nil {
log.Fatal(err)
}
fmt.Printf("%d: %s - ¥%.2f\n", id, name, price)
}
// 检查迭代错误
if err = rows.Err(); err != nil {
log.Fatal("遍历结果出错:", err)
}
# 4. 最佳实践与安全防护
# 4.1 SQL注入防御对照表
方法 | 安全性 | 示例 |
---|---|---|
字符串拼接 1 | ❌ 高风险 | "SELECT * FROM users WHERE id = " + userInput |
字符串拼接 2 | ❌ 高风险 | fmt.Sprintf("SELECT * FROM users WHERE id = %s", userInput) |
参数化查询(?占位) | ✅ 安全 | db.Query("... WHERE id = ?", userInput) |
预处理语句 | ✅ 安全+高性能 | 见下方示例 |
# 4.2 预处理语句使用
// 准备预处理语句
stmt, err := db.Prepare("INSERT INTO products (name, price) VALUES (?, ?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close() // 必须关闭
// 批量插入
products := []struct {
Name string
Price float64
}{
{"鼠标", 59.9},
{"键盘", 129.0},
{"显示器", 899.0},
}
for _, p := range products {
_, err := stmt.Exec(p.Name, p.Price)
if err != nil {
log.Printf("插入失败: %v", err)
}
}
# 4.3 NULL值处理方案
// 声明可空变量
var (
name sql.NullString
price sql.NullFloat64
)
// 扫描处理
err := db.QueryRow("SELECT name, price FROM products WHERE id = 101").Scan(
&name, &price,
)
if name.Valid {
fmt.Println("商品名:", name.String)
} else {
fmt.Println("商品名未设置")
}
if price.Valid {
fmt.Printf("价格: ¥%.2f", price.Float64)
} else {
fmt.Println("价格未设置")
}
# 4.4 事务处理范例
- 关键方法:
Begin()
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// 异常时回滚
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 执行事务操作
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100.0, 1)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100.0, 2)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
// 提交事务
if err = tx.Commit(); err != nil {
log.Fatal(err)
}