<返回目录     Powered by claud/xia兄

第14课: 数据库操作

database/sql包

Go标准库的database/sql包提供了通用的SQL数据库接口。

安装MySQL驱动

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

连接数据库

package main

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

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

func main() {
    // 连接字符串格式: username:password@tcp(host:port)/database
    dsn := "root:password@tcp(localhost:3306)/testdb"

    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(5)                  // 最大空闲连接数
db.SetConnMaxLifetime(5 * time.Minute) // 连接最大生命周期

CRUD操作

创建表

func createTable(db *sql.DB) error {
    query := `
    CREATE TABLE IF NOT EXISTS users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL,
        age INT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )`

    _, err := db.Exec(query)
    return err
}

插入数据

func insertUser(db *sql.DB, name, email string, age int) (int64, error) {
    query := "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"

    result, err := db.Exec(query, name, email, age)
    if err != nil {
        return 0, err
    }

    // 获取插入的ID
    id, err := result.LastInsertId()
    if err != nil {
        return 0, err
    }

    return id, nil
}

查询单行

type User struct {
    ID        int
    Name      string
    Email     string
    Age       int
    CreatedAt time.Time
}

func getUserByID(db *sql.DB, id int) (*User, error) {
    query := "SELECT id, name, email, age, created_at FROM users WHERE id = ?"

    user := &User{}
    err := db.QueryRow(query, id).Scan(
        &user.ID,
        &user.Name,
        &user.Email,
        &user.Age,
        &user.CreatedAt,
    )

    if err == sql.ErrNoRows {
        return nil, fmt.Errorf("用户不存在")
    }
    if err != nil {
        return nil, err
    }

    return user, nil
}

查询多行

func getAllUsers(db *sql.DB) ([]*User, error) {
    query := "SELECT id, name, email, age, created_at FROM users"

    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Name,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
        )
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }

    // 检查遍历过程中的错误
    if err = rows.Err(); err != nil {
        return nil, err
    }

    return users, nil
}

更新数据

func updateUser(db *sql.DB, id int, name, email string) error {
    query := "UPDATE users SET name = ?, email = ? WHERE id = ?"

    result, err := db.Exec(query, name, email, id)
    if err != nil {
        return err
    }

    // 检查影响的行数
    rowsAffected, err := result.RowsAffected()
    if err != nil {
        return err
    }

    if rowsAffected == 0 {
        return fmt.Errorf("用户不存在")
    }

    return nil
}

删除数据

func deleteUser(db *sql.DB, id int) error {
    query := "DELETE FROM users WHERE id = ?"

    result, err := db.Exec(query, id)
    if err != nil {
        return err
    }

    rowsAffected, err := result.RowsAffected()
    if err != nil {
        return err
    }

    if rowsAffected == 0 {
        return fmt.Errorf("用户不存在")
    }

    return nil
}

事务处理

func transferMoney(db *sql.DB, fromID, toID int, amount float64) error {
    // 开始事务
    tx, err := db.Begin()
    if err != nil {
        return err
    }

    // 确保事务被提交或回滚
    defer func() {
        if err != nil {
            tx.Rollback()
            return
        }
        err = tx.Commit()
    }()

    // 扣款
    _, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromID)
    if err != nil {
        return err
    }

    // 加款
    _, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toID)
    if err != nil {
        return err
    }

    return nil
}

GORM

GORM是Go最流行的ORM库,提供了更简洁的数据库操作API。

安装GORM

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

连接数据库

package main

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

func main() {
    dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"

    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic("连接数据库失败")
    }

    // 自动迁移
    db.AutoMigrate(&User{})
}

定义模型

type User struct {
    ID        uint           `gorm:"primaryKey"`
    Name      string         `gorm:"size:100;not null"`
    Email     string         `gorm:"size:100;uniqueIndex;not null"`
    Age       int
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

CRUD操作

// 创建
user := User{Name: "Alice", Email: "alice@example.com", Age: 25}
db.Create(&user)

// 查询单条
var user User
db.First(&user, 1)                    // 根据主键查询
db.First(&user, "email = ?", "alice@example.com") // 条件查询

// 查询多条
var users []User
db.Find(&users)                       // 查询所有
db.Where("age > ?", 18).Find(&users)  // 条件查询

// 更新
db.Model(&user).Update("name", "Bob")
db.Model(&user).Updates(User{Name: "Bob", Age: 30})

// 删除
db.Delete(&user, 1)                   // 软删除

关联查询

type User struct {
    gorm.Model
    Name   string
    Orders []Order
}

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

// 预加载
var users []User
db.Preload("Orders").Find(&users)

// 创建关联
user := User{Name: "Alice"}
order := Order{Amount: 100.0}
user.Orders = append(user.Orders, order)
db.Create(&user)

最佳实践

常见错误:

实践练习

  1. 使用database/sql实现完整的CRUD操作
  2. 实现事务处理和错误回滚
  3. 使用GORM重写数据库操作
  4. 实现分页查询功能
  5. 添加数据库连接池监控
  6. 实现软删除功能
  7. 优化查询性能(索引、预加载)
  8. 实现数据库迁移脚本