导航菜单

数据库操作

Go 通过 database/sql 标准库提供了统一的数据库操作接口,支持 MySQL、PostgreSQL、SQLite 等多种数据库。同时,GORM 是 Go 生态中最流行的 ORM 框架。

database/sql 标准库

database/sql

database/sql 是 Go 标准库中的数据库操作包,它提供了一组通用的接口,具体的数据库驱动只需实现这些接口即可。这意味着你可以切换数据库而几乎不需要修改业务代码。

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql" // 匿名导入,仅调用 init() 注册驱动
)

连接数据库

package main

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

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

func main() {
    // DSN 格式: username:password@tcp(host:port)/dbname?params
    dsn := "root:password@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True"

    db, err := sql.Open("mysql", dsn)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 验证连接
    if err := db.Ping(); err != nil {
        log.Fatal(err)
    }
    fmt.Println("数据库连接成功")
}

连接池配置

db.SetMaxOpenConns(25)          // 最大打开连接数
db.SetMaxIdleConns(25)           // 最大空闲连接数
db.SetConnMaxLifetime(5 * time.Minute) // 连接最大存活时间
db.SetConnMaxIdleTime(10 * time.Minute) // 空闲连接最大存活时间

CRUD 操作

// Create - 插入数据
result, err := db.Exec("INSERT INTO users (name, email, age) VALUES (?, ?, ?)", "张三", "zhangsan@example.com", 25)
id, _ := result.LastInsertId()

// Read - 查询单行
var name, email string
var age int
err := db.QueryRow("SELECT name, email, age FROM users WHERE id = ?", id).Scan(&name, &email, &age)

// Read - 查询多行
rows, err := db.Query("SELECT id, name, email FROM users WHERE age > ?", 18)
defer rows.Close()
for rows.Next() {
    var u User
    rows.Scan(&u.ID, &u.Name, &u.Email)
}

// Update - 更新数据
result, err := db.Exec("UPDATE users SET age = ? WHERE id = ?", 30, id)
affected, _ := result.RowsAffected()

// Delete - 删除数据
result, err := db.Exec("DELETE FROM users WHERE id = ?", id)

预编译语句(防 SQL 注入)

// 使用 Prepare 预编译 SQL
stmt, err := db.Prepare("INSERT INTO users (name, email) VALUES (?, ?)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close()

// 多次执行预编译语句(高效)
stmt.Exec("张三", "zhangsan@example.com")
stmt.Exec("李四", "lisi@example.com")

事务处理

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}

// 在事务中执行操作
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100, 1)
if err != nil {
    tx.Rollback() // 出错时回滚
    log.Fatal(err)
}

_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100, 2)
if err != nil {
    tx.Rollback()
    log.Fatal(err)
}

tx.Commit() // 全部成功时提交

GORM —— ORM 框架

GORM 是 Go 最流行的全功能 ORM,支持自动迁移、关联关系、钩子、复杂查询等。

安装与连接

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

dsn := "root:password@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

模型定义与迁移

type User struct {
    gorm.Model
    Name   string `gorm:"size:100;not null" json:"name"`
    Email  string `gorm:"size:200;uniqueIndex" json:"email"`
    Age    int    `gorm:"default:0" json:"age"`
}

// 自动迁移(创建/更新表结构)
db.AutoMigrate(&User{})

CRUD 操作

// Create
user := User{Name: "张三", Email: "zhangsan@example.com", Age: 25}
db.Create(&user)

// Read
var u User
db.First(&u, 1)                    // 按 ID 查询
db.Where("email = ?", "zhangsan@example.com").First(&u)

// Update
db.Model(&u).Update("age", 30)
db.Model(&u).Updates(User{Name: "新名字", Age: 31})

// Delete
db.Delete(&u)

关联关系

type User struct {
    gorm.Model
    Name      string
    Orders    []Order    `gorm:"foreignKey:UserID"`
    Profile   *Profile   `gorm:"foreignKey:UserID"`
}

type Order struct {
    gorm.Model
    UserID  uint
    Amount  float64
}

type Profile struct {
    gorm.Model
    UserID uint
    Bio    string
}

// 预加载关联
db.Preload("Orders").Find(&users)
db.Preload("Orders").Preload("Profile").First(&user, 1)

数据库事务

db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&Order{UserID: 1, Amount: 100}).Error; err != nil {
        return err // 自动回滚
    }
    if err := tx.Model(&Account{}).Where("user_id = ?", 1).Update("balance", gorm.Expr("balance - ?", 100)).Error; err != nil {
        return err // 自动回滚
    }
    return nil // 自动提交
})

练习题

练习 1

使用 database/sql 编写一个程序:1) 创建 products 表(id, name, price, stock);2) 插入 3 条数据;3) 查询价格大于 50 的产品;4) 更新某产品的库存;5) 删除某条记录。所有操作在一个事务中完成。

参考答案 (3 个标签)
database/sql 事务 CRUD
package main

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

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

func main() {
    db, err := sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/mydb")
    if err != nil { log.Fatal(err) }
    defer db.Close()

    tx, _ := db.Begin()

    // 建表
    tx.Exec(`CREATE TABLE IF NOT EXISTS products (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        price DECIMAL(10,2) NOT NULL,
        stock INT DEFAULT 0
    )`)

    // 插入
    tx.Exec("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)", "手机", 3999.00, 100)
    tx.Exec("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)", "耳机", 299.00, 500)
    tx.Exec("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)", "充电宝", 69.00, 1000)

    // 查询
    rows, _ := tx.Query("SELECT name, price FROM products WHERE price > ?", 50)
    defer rows.Close()
    for rows.Next() {
        var name string; var price float64
        rows.Scan(&name, &price)
        fmt.Printf("%s: ¥%.2f\n", name, price)
    }

    // 更新
    tx.Exec("UPDATE products SET stock = stock - 1 WHERE name = ?", "手机")

    // 删除
    tx.Exec("DELETE FROM products WHERE name = ?", "充电宝")

    tx.Commit()
    fmt.Println("事务执行成功")
}

搜索