《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)

news2025/8/17 13:02:35

在这里插入图片描述

文章目录

  • 9.1 连接数据库 - Go 语言的海底宝藏之门
    • 9.1.1 基础知识讲解
      • 安装数据库驱动
      • 数据库连接
    • 9.1.2 重点案例:用户信息管理系统
      • 准备数据库
      • Go 代码实现
        • 连接数据库
        • 添加新用户
        • 查询用户信息
        • 用户登录验证
        • 主函数
    • 9.1.3 拓展案例 1:批量添加用户
      • 准备数据库
      • Go 代码实现
        • 实现批量添加用户功能
        • 主函数
    • 9.1.4 拓展案例 2:用户登录验证
      • 准备数据库
      • Go 代码实现
        • 生成和验证密码哈希
        • 实现用户登录验证
        • 主函数
  • 9.2 执行查询与操作数据 - Go 语言的数据潜水艇
    • 9.2.1 基础知识讲解
    • 9.2.2 重点案例:图书管理系统
      • 准备数据库
      • Go 代码实现
        • 连接数据库
        • 添加新图书
        • 查询图书列表
        • 更新图书信息
        • 删除图书记录
        • 主函数
    • 9.2.3 拓展案例 1:图书借阅记录
      • 准备数据库
      • Go 代码实现
        • 添加用户
        • 添加借阅记录
        • 查询借阅记录
        • 主函数
    • 9.2.4 拓展案例 2:图书搜索功能
      • 准备数据库
      • Go 代码实现
        • 实现搜索图书功能
        • 主函数
  • 9.3 使用ORM框架 - Go 语言的数据航海术
    • 9.3.1 基础知识讲解
      • GORM 介绍
      • 连接数据库
    • 9.3.2 重点案例:员工管理系统
      • 定义模型
      • 初始化数据库并自动迁移模型
      • 实现员工的增删改查操作
      • 主函数
    • 9.3.3 拓展案例 1:员工请假记录
      • 扩展数据库模型
      • 实现请假记录操作
        • 添加请假记录
        • 查询员工的请假记录
      • 主函数
    • 9.3.4 拓展案例 2:部门管理
      • 扩展数据库模型
      • 实现部门管理操作
        • 添加部门
        • 查询部门列表
        • 将员工分配到部门
      • 主函数

9.1 连接数据库 - Go 语言的海底宝藏之门

9.1.1 基础知识讲解

在Go语言的海洋中,连接数据库是获取深海宝藏的第一步。Go通过database/sql包提供了一个通用的接口来与SQL数据库进行交云,但实际上你还需要一个具体数据库的驱动来完成这个任务。

安装数据库驱动

首先,选择并安装你需要的数据库驱动。对于MySQL,你可能会选择github.com/go-sql-driver/mysql

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

对于PostgreSQL,则可能是github.com/lib/pq

go get -u github.com/lib/pq

数据库连接

连接数据库通常涉及创建一个sql.DB对象,它代表一个数据库的长连接。使用sql.Open函数并传入相应的驱动名和数据库的DSN(数据源名称)即可:

db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

确保在不再需要数据库连接时调用db.Close()来关闭连接。

9.1.2 重点案例:用户信息管理系统

在这个扩展案例中,我们将构建一个用户信息管理系统,该系统将演示如何在Go语言中连接MySQL数据库、添加新用户以及查询现有用户信息。此外,我们还将实现一个简单的用户登录验证功能。

准备数据库

首先,确保你的MySQL数据库服务正在运行,并执行以下SQL脚本来创建数据库和表:

CREATE DATABASE IF NOT EXISTS userdb;
USE userdb;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL
);

Go 代码实现

在Go项目中,使用database/sql包和github.com/go-sql-driver/mysql驱动来实现与数据库的连接和操作。

连接数据库

首先,实现一个函数来创建数据库连接:

// db.go

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func connectDB() *sql.DB {
    db, err := sql.Open("mysql", "user:password@/userdb?parseTime=true")
    if err != nil {
        log.Fatal(err)
    }
    return db
}
添加新用户

实现一个函数,用于添加新用户到数据库:

// user.go

package main

import (
    "log"
)

func addUser(db *sql.DB, name, email, password string) {
    _, err := db.Exec("INSERT INTO users (name, email, password) VALUES (?, ?, ?)", name, email, password)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("New user added successfully:", name)
}
查询用户信息

实现一个函数,用于根据邮箱查询用户信息:

func getUserByEmail(db *sql.DB, email string) {
    var name, userEmail string
    err := db.QueryRow("SELECT name, email FROM users WHERE email = ?", email).Scan(&name, &userEmail)
    if err != nil {
        log.Println("User not found:", err)
        return
    }
    log.Printf("User found: Name: %s, Email: %s\n", name, userEmail)
}
用户登录验证

实现一个简单的用户登录验证功能:

func verifyUserLogin(db *sql.DB, email, password string) {
    var dbPassword string
    err := db.QueryRow("SELECT password FROM users WHERE email = ?", email).Scan(&dbPassword)
    if err != nil {
        log.Println("Authentication failed:", err)
        return
    }
    if dbPassword == password {
        log.Println("User authenticated successfully")
    } else {
        log.Println("Invalid password")
    }
}
主函数

main函数中,调用上述函数演示添加新用户和查询用户信息的功能:

// main.go

package main

func main() {
    db := connectDB()
    defer db.Close()

    addUser(db, "Jack Sparrow", "jack@sparrow.com", "pirate")
    getUserByEmail(db, "jack@sparrow.com")
    verifyUserLogin(db, "jack@sparrow.com", "pirate")
}

通过运行main.go文件,你可以看到添加新用户、查询用户信息和用户登录验证的过程。

通过这个扩展案例,你已经学会了如何在Go语言中连接MySQL数据库、执行基本的数据操作以及实现简单的用户登录验证功能。这为构建更复杂的Web应用和服务提供了坚实的基础。随着你对数据库操作的深入,你将能够更有效地管理和利用数据,为用户提供丰富和安全的服务。

9.1.3 拓展案例 1:批量添加用户

在这个案例中,我们将扩展之前的用户信息管理系统,实现一个功能以批量添加用户到数据库中。这项功能非常适合于需要一次性导入大量用户数据的场景,比如从CSV文件导入或者在系统初始化时预填充用户数据。为了保证数据的一致性和完整性,我们将使用事务来处理批量添加操作。

准备数据库

确保你已经按照之前的指示创建了userdb数据库和users表。

Go 代码实现

实现批量添加用户功能

我们将实现一个addUsers函数,接收一个用户信息列表,并使用事务批量添加用户到数据库中:

// user.go

package main

import (
    "database/sql"
    "log"
)

type User struct {
    Name     string
    Email    string
    Password string
}

func addUsers(db *sql.DB, users []User) {
    tx, err := db.Begin()
    if err != nil {
        log.Fatal("Failed to begin transaction:", err)
    }

    stmt, err := tx.Prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)")
    if err != nil {
        log.Fatal("Failed to prepare statement:", err)
    }
    defer stmt.Close()

    for _, user := range users {
        _, err = stmt.Exec(user.Name, user.Email, user.Password)
        if err != nil {
            log.Fatal("Failed to execute statement:", err)
        }
    }

    err = tx.Commit()
    if err != nil {
        log.Fatal("Failed to commit transaction:", err)
    }

    log.Println("Users added successfully")
}

在这个函数中,我们首先开始一个新的事务。对于传入的每个用户,我们使用预编译的SQL语句和事务执行插入操作。最后,如果所有插入操作都成功执行,我们提交事务;如果在过程中遇到任何错误,事务将被回滚,保证数据库状态的一致性。

主函数

main函数中,调用addUsers函数演示批量添加用户的功能:

// main.go

package main

func main() {
    db := connectDB()
    defer db.Close()

    users := []User{
        {"Will Turner", "will@turner.com", "Bootstrap"},
        {"Elizabeth Swann", "elizabeth@swann.com", "PirateKing"},
        {"Hector Barbossa", "hector@barbossa.com", "BlackPearl"},
    }

    addUsers(db, users)
}

通过运行main.go文件,你可以看到批量添加用户的过程。使用事务可以确保批量添加操作的原子性,即要么所有用户都成功添加,要么一个都不添加,避免了可能出现的数据不一致问题。

通过这个拓展案例,你已经学会了如何在Go语言中实现批量添加用户的功能,并了解了事务在保证数据库操作一致性中的重要作用。掌握这些技能后,你将能够更高效地管理和操作数据库中的数据。

9.1.4 拓展案例 2:用户登录验证

在这个案例中,我们将为用户信息管理系统添加用户登录验证功能。这个功能允许用户通过提供邮箱和密码来验证其身份。为了保证安全性,我们将存储密码的哈希值,而不是明文密码。

准备数据库

首先,确保你的users表已经准备好,并且有一个用于存储密码哈希值的字段:

ALTER TABLE users ADD COLUMN password_hash VARCHAR(255) NOT NULL;

Go 代码实现

为了处理密码,我们将使用golang.org/x/crypto/bcrypt包来生成和验证密码哈希。首先,安装bcrypt包:

go get -u golang.org/x/crypto/bcrypt
生成和验证密码哈希

我们将实现两个函数:一个用于生成密码的哈希值,另一个用于验证提供的密码是否与存储的哈希值匹配:

// auth.go

package main

import (
    "golang.org/x/crypto/bcrypt"
)

// GeneratePasswordHash 生成密码的哈希值
func GeneratePasswordHash(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    return string(bytes), err
}

// VerifyPassword 检查密码是否与其哈希值匹配
func VerifyPassword(password, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}
实现用户登录验证

接下来,我们扩展用户信息管理系统,添加用户登录验证功能:

// user.go

package main

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

// VerifyUserLogin 验证用户登录
func VerifyUserLogin(db *sql.DB, email, password string) {
    var passwordHash string
    err := db.QueryRow("SELECT password_hash FROM users WHERE email = ?", email).Scan(&passwordHash)
    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Println("用户不存在")
        } else {
            log.Fatal("查询错误:", err)
        }
        return
    }

    if VerifyPassword(password, passwordHash) {
        fmt.Println("登录成功")
    } else {
        fmt.Println("密码错误")
    }
}
主函数

main函数中,调用VerifyUserLogin函数演示用户登录验证的过程:

// main.go

package main

func main() {
    db := connectDB()
    defer db.Close()

    // 假设用户已经被添加到数据库,并且密码哈希已经存储
    email := "jack@sparrow.com"
    password := "pirate"

    VerifyUserLogin(db, email, password)
}

通过运行main.go文件,你可以测试用户登录验证的功能。这个过程首先查询用户的密码哈希值,然后使用bcrypt库来验证提供的密码是否与哈希值匹配。

通过这个拓展案例,你已经学会了如何在Go语言中实现基本的用户登录验证功能,包括如何安全地处理和存储密码。这为构建需要用户认证的Web应用和服务提供了坚实的基础。随着你对安全性的深入理解,你将能够为你的应用实现更高级的安全特性。

9.2 执行查询与操作数据 - Go 语言的数据潜水艇

9.2.1 基础知识讲解

在Go的数据海洋中,执行查询和操作数据是寻找和管理宝藏的关键步骤。database/sql包提供了一套通用的接口,允许我们执行SQL查询、插入、更新和删除操作,以及处理结果集。

执行查询

查询数据库并处理结果集通常包括以下几个步骤:

  1. 使用QueryQueryRow执行SQL查询Query用于返回多行结果,而QueryRow用于期望最多一行结果的查询。

  2. 遍历Rows结果集(如果是多行结果)。使用Next方法迭代结果集,并使用Scan方法将行数据扫描到变量中。

  3. 处理Row结果(如果是单行结果)。直接使用Scan方法将结果扫描到变量中。

操作数据

插入、更新和删除操作通常使用Exec方法执行SQL命令。这个方法返回一个Result对象,它提供了关于操作影响的行数等信息。

9.2.2 重点案例:图书管理系统

在这个扩展案例中,我们将构建一个简单的图书管理系统,该系统将演示如何在Go语言中连接MySQL数据库、添加新图书、查询图书列表、更新图书信息以及删除图书记录。

准备数据库

首先,确保你的MySQL数据库服务正在运行,并执行以下SQL脚本来创建数据库和books表:

CREATE DATABASE IF NOT EXISTS library;
USE library;
CREATE TABLE IF NOT EXISTS books (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    author VARCHAR(255) NOT NULL,
    published_date DATE
);

Go 代码实现

连接数据库

首先,实现一个函数来创建数据库连接:

// db.go

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func connectDB() *sql.DB {
    db, err := sql.Open("mysql", "user:password@/library")
    if err != nil {
        log.Fatal(err)
    }
    return db
}

确保替换user:password@/library中的userpassword为你的数据库用户名和密码。

添加新图书
// book.go

package main

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

func addBook(db *sql.DB, title, author string, publishedDate time.Time) {
    _, err := db.Exec("INSERT INTO books (title, author, published_date) VALUES (?, ?, ?)", title, author, publishedDate)
    if err != nil {
        log.Fatal("Failed to add book:", err)
    }
    fmt.Println("Successfully added new book:", title)
}
查询图书列表
func listBooks(db *sql.DB) {
    rows, err := db.Query("SELECT id, title, author, published_date FROM books")
    if err != nil {
        log.Fatal("Failed to list books:", err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var title, author string
        var publishedDate time.Time
        if err := rows.Scan(&id, &title, &author, &publishedDate); err != nil {
            log.Fatal("Failed to scan book:", err)
        }
        fmt.Printf("%d: %s by %s, published on %s\n", id, title, author, publishedDate.Format("2006-01-02"))
    }
}
更新图书信息
func updateBook(db *sql.DB, id int, title, author string) {
    _, err := db.Exec("UPDATE books SET title = ?, author = ? WHERE id = ?", title, author, id)
    if err != nil {
        log.Fatal("Failed to update book:", err)
    }
    fmt.Println("Successfully updated book with ID:", id)
}
删除图书记录
func deleteBook(db *sql.DB, id int) {
    _, err := db.Exec("DELETE FROM books WHERE id = ?", id)
    if err != nil {
        log.Fatal("Failed to delete book:", err)
    }
    fmt.Println("Successfully deleted book with ID:", id)
}
主函数

main.go中,整合上述功能,并运行演示:

// main.go

package main

import "time"

func main() {
    db := connectDB()
    defer db.Close()

    // 添加图书示例
    addBook(db, "Go Programming Language", "Alan A. A. Donovan & Brian W. Kernighan", time.Date(2015, 11, 17, 0, 0, 0, 0, time.UTC))

    // 查询图书列表
    listBooks(db)

    // 更新图书信息
    updateBook(db, 1, "The Go Programming Language", "Alan A. A. Donovan & Brian W. Kernighan")

    // 删除图书记录
    deleteBook(db, 1)
}

通过运行main.go文件,你可以测试图书管理系统的各项功能。这个简单的应用展示了在Go中如何执行常见的数据库操作,包括连接数据库、执行SQL命令以及处理查询结果。

通过这个案例,你学会了如何使用Go与数据库进行交互,实现一个基本的图书管理系统。这为进一步开发更复杂的数据库驱动应用奠定了基础。随着对数据库操作的深入掌握,你将能够构建更加强大和灵活的数据管理和分析功能。

9.2.3 拓展案例 1:图书借阅记录

在这个案例中,我们将扩展图书管理系统,添加功能以记录用户的图书借阅信息。这项功能涉及到创建新的数据库表来存储借阅记录,并实现相关的增、查操作。

准备数据库

首先,除了已有的books表之外,我们还需要创建两个新的表:usersborrow_records

CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL
);

CREATE TABLE IF NOT EXISTS borrow_records (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    book_id INT NOT NULL,
    borrow_date DATE NOT NULL,
    return_date DATE,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (book_id) REFERENCES books(id)
);

Go 代码实现

添加用户

首先,我们需要能够添加用户到users表中,因为借阅记录需要关联到具体的用户。

// user.go

func addUser(db *sql.DB, name, email string) {
    _, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", name, email)
    if err != nil {
        log.Fatal("Failed to add user:", err)
    }
    fmt.Println("User added successfully:", name)
}
添加借阅记录

接下来,实现添加借阅记录的功能:

// borrow.go

func addBorrowRecord(db *sql.DB, userID, bookID int, borrowDate time.Time) {
    _, err := db.Exec("INSERT INTO borrow_records (user_id, book_id, borrow_date) VALUES (?, ?, ?)", userID, bookID, borrowDate)
    if err != nil {
        log.Fatal("Failed to add borrow record:", err)
    }
    fmt.Println("Borrow record added successfully for user ID:", userID)
}
查询借阅记录

实现一个函数,用于查询特定用户的所有借阅记录:

func listBorrowRecords(db *sql.DB, userID int) {
    rows, err := db.Query("SELECT b.id, bk.title, b.borrow_date, b.return_date FROM borrow_records b JOIN books bk ON b.book_id = bk.id WHERE b.user_id = ?", userID)
    if err != nil {
        log.Fatal("Failed to list borrow records:", err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var title string
        var borrowDate, returnDate sql.NullTime
        if err := rows.Scan(&id, &title, &borrowDate, &returnDate); err != nil {
            log.Fatal("Failed to scan borrow record:", err)
        }
        fmt.Printf("Record %d: %s, Borrowed on %s, Returned on %v\n", id, title, borrowDate.Time.Format("2006-01-02"), returnDate.Time.Format("2006-01-02"))
    }
}
主函数

main.go中,整合上述功能,并运行演示:

// main.go

package main

func main() {
    db := connectDB()
    defer db.Close()

    // 添加用户示例
    addUser(db, "Elizabeth Swann", "elizabeth@swann.com")

    // 添加图书示例
    addBook(db, "Pirates of the Caribbean", "Ted Elliott & Terry Rossio", time.Date(2003, 7, 9, 0, 0, 0, 0, time.UTC))

    // 添加借阅记录
    addBorrowRecord(db, 1, 1, time.Now())

    // 查询借阅记录
    listBorrowRecords(db, 1)
}

通过运行main.go文件,你可以测试添加用户、图书以及借阅记录的过程,并且能够查询特定用户的所有借阅记录。

通过这个拓展案例,你已经学会了如何在Go语言中实现与数据库相关的更复杂的操作,包括处理多表关联和实现具体的业务逻辑。随着你对数据库操作的掌握越来越深入,你将能够构建更加复杂和功能丰富的应用。

9.2.4 拓展案例 2:图书搜索功能

在这个案例中,我们将扩展图书管理系统,添加图书搜索功能,允许用户通过书名或作者名进行搜索。这项功能能够帮助用户快速找到感兴趣的图书,提高系统的用户体验。

准备数据库

假设我们已经有了一个books表,其中包含图书的标题(title)和作者(author)。

Go 代码实现

实现搜索图书功能

我们将实现一个searchBooks函数,接收一个搜索关键字,并在书名和作者名中查找匹配项。

// search.go

package main

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

func searchBooks(db *sql.DB, keyword string) {
    // 使用LIKE操作符进行模糊匹配,同时搜索书名和作者名
    query := fmt.Sprintf("%%s%", keyword) // 构建一个包含通配符的查询字符串
    rows, err := db.Query("SELECT id, title, author FROM books WHERE title LIKE ? OR author LIKE ?", query, query)
    if err != nil {
        log.Fatal("Failed to search books:", err)
    }
    defer rows.Close()

    fmt.Println("Search results:")
    for rows.Next() {
        var id int
        var title, author string
        if err := rows.Scan(&id, &title, &author); err != nil {
            log.Fatal("Failed to scan book:", err)
        }
        fmt.Printf("ID: %d, Title: %s, Author: %s\n", id, title, author)
    }

    // 检查是否有错误发生在迭代结果集中
    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }
}

请注意,搜索时使用LIKE操作符和通配符%来实现模糊匹配,以便匹配包含关键字的所有图书。

主函数

main.go中,调用searchBooks函数演示搜索图书的功能:

// main.go

package main

func main() {
    db := connectDB()
    defer db.Close()

    // 示例:搜索图书
    searchKeyword := "Caribbean"
    searchBooks(db, searchKeyword)
}

通过运行main.go文件,你可以测试图书搜索功能。输入一个关键字,程序将列出所有在书名或作者名中包含该关键字的图书。

通过这个拓展案例,你已经学会了如何在Go语言中为图书管理系统实现一个基本的搜索功能。这种功能在现实世界的应用中非常有用,尤其是在数据量大的情况下,能够帮助用户快速定位到他们感兴趣的信息。随着你对数据库操作和查询优化的进一步学习和实践,你将能够构建出更加强大和高效的搜索功能。

9.3 使用ORM框架 - Go 语言的数据航海术

9.3.1 基础知识讲解

在Go的数据探险旅程中,使用ORM(Object-Relational Mapping)框架可以让我们以更直观和对象化的方式与数据库进行交互,而无需编写繁琐的SQL语句。ORM框架提供了一种机制,将数据库表映射为Go中的结构体,数据库表中的行(记录)映射为结构体的实例,这样我们就可以使用Go代码来操作数据库了。

GORM 介绍

GORM是Go语言中最受欢迎的ORM库之一,它具有全功能的ORM功能,支持关联(包括一对一、一对多、多对多)、钩子(回调)、事务、迁移、SQL构建器、自动加载和预加载等特性。

要使用GORM,首先需要安装:

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite // 对于SQLite
go get -u gorm.io/driver/mysql // 对于MySQL
go get -u gorm.io/driver/postgres // 对于PostgreSQL

连接数据库

使用GORM连接数据库非常简单,以下是一个连接SQLite数据库的示例:

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

db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
    panic("failed to connect database")
}

9.3.2 重点案例:员工管理系统

在这个扩展案例中,我们将构建一个简单的员工管理系统,使用GORM实现对员工数据的增删改查操作。该系统将演示如何在Go语言中使用ORM框架来简化数据库操作,提高开发效率。

定义模型

首先,我们定义Employee模型来映射数据库中的employees表:

// models.go

package main

import (
    "gorm.io/gorm"
    "time"
)

type Employee struct {
    ID        uint           `gorm:"primaryKey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
    Name      string
    Email     string         `gorm:"uniqueIndex"`
    Position  string
}

初始化数据库并自动迁移模型

接下来,初始化数据库连接并自动迁移模型,确保数据库表结构与Go中的模型结构一致:

// db.go

package main

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
)

func InitDB() *gorm.DB {
    db, err := gorm.Open(sqlite.Open("employees.db"), &gorm.Config{})
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }

    // 自动迁移模式
    db.AutoMigrate(&Employee{})

    return db
}

实现员工的增删改查操作

在员工管理系统中,我们需要实现以下功能:

  • 添加新员工
func AddEmployee(db *gorm.DB, employee *Employee) {
    result := db.Create(employee)
    if result.Error != nil {
        log.Fatalf("Failed to add employee: %v", result.Error)
    }
    log.Printf("Added new employee: %s", employee.Name)
}
  • 查询所有员工
func ListEmployees(db *gorm.DB) {
    var employees []Employee
    result := db.Find(&employees)
    if result.Error != nil {
        log.Fatalf("Failed to list employees: %v", result.Error)
    }
    for _, employee := range employees {
        log.Printf("ID: %d, Name: %s, Email: %s, Position: %s", employee.ID, employee.Name, employee.Email, employee.Position)
    }
}
  • 更新员工信息
func UpdateEmployee(db *gorm.DB, id uint, updateData map[string]interface{}) {
    result := db.Model(&Employee{}).Where("id = ?", id).Updates(updateData)
    if result.Error != nil {
        log.Fatalf("Failed to update employee: %v", result.Error)
    }
    log.Printf("Updated employee with ID: %d", id)
}
  • 删除员工
func DeleteEmployee(db *gorm.DB, id uint) {
    result := db.Delete(&Employee{}, id)
    if result.Error != nil {
        log.Fatalf("Failed to delete employee: %v", result.Error)
    }
    log.Printf("Deleted employee with ID: %d", id)
}

主函数

main.go中,我们整合上述功能,并运行演示:

// main.go

package main

func main() {
    db := InitDB()

    // 添加新员工
    newEmployee := Employee{Name: "Jack Sparrow", Email: "jack@sparrow.com", Position: "Captain"}
    AddEmployee(db, &newEmployee)

    // 查询所有员工
    ListEmployees(db)

    // 更新员工信息
    UpdateEmployee(db, newEmployee.ID, map[string]interface{}{"Position": "Pirate Lord"})

    // 删除员工
    DeleteEmployee(db, newEmployee.ID)
}

通过运行main.go文件,你可以测试员工管理系统的各项功能。这个简单的应用展示了如何使用GORM与SQLite数据库进行交互,实现对员工数据的操作。

通过这个案例,你已经学会了如何在Go语言中使用GORM框架来简化数据库操作。随着对GORM和ORM概念的深入理解,你将能够更高效地开发出数据驱动的应用。

9.3.3 拓展案例 1:员工请假记录

在这个拓展案例中,我们将为员工管理系统添加员工请假记录的功能。这项功能涉及到新的数据库表leave_records的创建,并实现员工请假记录的增加和查询操作。

扩展数据库模型

首先,我们需要创建一个新的表leave_records来存储请假记录,并且这个表与employees表通过外键关联。

CREATE TABLE IF NOT EXISTS leave_records (
    id INT AUTO_INCREMENT PRIMARY KEY,
    employee_id INT NOT NULL,
    leave_date DATE NOT NULL,
    return_date DATE NOT NULL,
    reason TEXT,
    FOREIGN KEY (employee_id) REFERENCES employees(id)
);

接着,在Go中定义LeaveRecord模型以映射leave_records表:

// models.go

type LeaveRecord struct {
    gorm.Model
    EmployeeID uint
    LeaveDate  time.Time
    ReturnDate time.Time
    Reason     string
}

确保在初始化数据库时自动迁移新模型:

db.AutoMigrate(&Employee{}, &LeaveRecord{})

实现请假记录操作

添加请假记录

我们实现一个函数来添加新的请假记录:

func AddLeaveRecord(db *gorm.DB, record *LeaveRecord) {
    if err := db.Create(record).Error; err != nil {
        log.Fatalf("Failed to add leave record: %v", err)
    } else {
        fmt.Println("Leave record added successfully.")
    }
}
查询员工的请假记录

实现一个函数来查询特定员工的所有请假记录:

func ListLeaveRecords(db *gorm.DB, employeeID uint) {
    var records []LeaveRecord
    if err := db.Where("employee_id = ?", employeeID).Find(&records).Error; err != nil {
        log.Fatalf("Failed to list leave records: %v", err)
    } else {
        for _, record := range records {
            fmt.Printf("Leave from %s to %s, Reason: %s\n", record.LeaveDate.Format("2006-01-02"), record.ReturnDate.Format("2006-01-02"), record.Reason)
        }
    }
}

主函数

main.go中,调用上述函数演示添加和查询请假记录的功能:

// main.go

package main

func main() {
    db := InitDB()

    // 假设已存在员工Jack Sparrow,其ID为1
    employeeID := uint(1)
    
    // 添加请假记录
    record := LeaveRecord{
        EmployeeID: employeeID,
        LeaveDate:  time.Date(2023, 1, 10, 0, 0, 0, 0, time.UTC),
        ReturnDate: time.Date(2023, 1, 20, 0, 0, 0, 0, time.UTC),
        Reason:     "Sailing the seven seas",
    }
    AddLeaveRecord(db, &record)

    // 查询员工的请假记录
    ListLeaveRecords(db, employeeID)
}

通过运行main.go文件,你可以测试添加和查询请假记录的功能。这个拓展案例展示了如何使用GORM处理更复杂的数据模型和关联关系,为员工管理系统添加新的功能。

通过这个案例,你进一步理解了在Go语言中使用ORM框架进行数据库操作的便利性,以及如何为应用添加实际业务逻辑。随着你对GORM和数据库操作的深入掌握,你将能够构建更加复杂和功能丰富的应用。

9.3.4 拓展案例 2:部门管理

在这个案例中,我们将为员工管理系统添加部门管理功能,允许创建部门、查询部门列表、将员工分配到部门等。这涉及到处理多对多的关联关系,并展示如何使用GORM进行复杂的查询和更新操作。

扩展数据库模型

首先,我们需要定义Department模型并创建一个新表departments来存储部门信息。同时,因为一个员工可以属于多个部门,一个部门也可以有多个员工,我们还需要一个关联表employee_departments来实现多对多的关系。

CREATE TABLE IF NOT EXISTS departments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    description TEXT
);

CREATE TABLE IF NOT EXISTS employee_departments (
    employee_id INT,
    department_id INT,
    FOREIGN KEY (employee_id) REFERENCES employees(id),
    FOREIGN KEY (department_id) REFERENCES departments(id),
    PRIMARY KEY (employee_id, department_id)
);

接着,在Go中定义Department模型并更新Employee模型以映射新的关系:

// models.go

type Department struct {
    gorm.Model
    Name        string
    Description string
    Employees   []Employee `gorm:"many2many:employee_departments;"`
}

type Employee struct {
    gorm.Model
    Name       string
    Email      string `gorm:"uniqueIndex"`
    Position   string
    Departments []Department `gorm:"many2many:employee_departments;"`
}

确保在初始化数据库时自动迁移新模型:

db.AutoMigrate(&Employee{}, &Department{})

实现部门管理操作

添加部门
func AddDepartment(db *gorm.DB, department *Department) {
    if err := db.Create(department).Error; err != nil {
        log.Fatalf("Failed to add department: %v", err)
    } else {
        fmt.Println("Department added successfully:", department.Name)
    }
}
查询部门列表
func ListDepartments(db *gorm.DB) {
    var departments []Department
    if err := db.Find(&departments).Error; err != nil {
        log.Fatalf("Failed to list departments: %v", err)
    } else {
        for _, department := range departments {
            fmt.Printf("ID: %d, Name: %s, Description: %s\n", department.ID, department.Name, department.Description)
        }
    }
}
将员工分配到部门
func AssignEmployeeToDepartment(db *gorm.DB, employeeID, departmentID uint) {
    var employee Employee
    var department Department
    if err := db.First(&employee, employeeID).Error; err != nil {
        log.Fatalf("Employee not found: %v", err)
    }
    if err := db.First(&department, departmentID).Error; err != nil {
        log.Fatalf("Department not found: %v", err)
    }

    db.Model(&employee).Association("Departments").Append(&department)
    fmt.Printf("Employee %s assigned to department %s\n", employee.Name, department.Name)
}

主函数

main.go中,整合上述功能,并运行演示:

// main.go

package main

func main() {
    db := InitDB()

    // 添加部门
    dept := Department{Name: "IT", Description: "Information Technology"}
    AddDepartment(db, &dept)

    // 查询部门列表
    ListDepartments(db)

    // 将员工分配到部门
    AssignEmployeeToDepartment(db, 1, 1) // 假设员工ID和部门ID都为1
}

通过运行main.go文件,你可以测试部门管理的各项功能。这个拓展案例展示了如何使用GORM处理多对多的关系,并在应用中实现复杂的业务逻辑。

通过这个案例,你已经学会了如何在Go语言中使用GORM框架来管理多对多的关系,并实现了一个部门管理功能。随着你对GORM和数据库操作的深入掌握,你将能够构建更加复杂和功能丰富的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1453408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【IIS中绑定SSL证书】

下载SSL证书: 打开服务器IIS: 点击导入 在IIS中新增网站:

Sentinel注解@SentinelResource详解

Sentinel注解SentinelResource详解 熔断 针对访问超过限制【sentinel中配置的限制】的资源,通过java代码配置,返回一个自定义的结果,需要用到 SentinelResource 注解的 blockHandlerClass 和 blockHandler 属性。 blockHandlerClass&#…

防御保护--内容安全过滤

目录 文件过滤 内容过滤技术 邮件过滤技术 应用行为控制技术 DNS过滤 URL过滤 防火墙 ---- 四层会话追踪技术 入侵防御 ---- 应用层深度检测技术 深度包检测深度流检测 随着以上俩种的成熟与完善,提出了所谓的内容安全过滤 当然上网行为确实需要治理&…

反转链表【基础算法精讲 06】

视频地址 反转链表【基础算法精讲 06】_哔哩哔哩_bilibili 概念 链表的每一个结点都包含节点值 和1指向下一个结点的next指针 , 链表的最后一个结点指向空; 206 . 反转链表 用cur记录当前遍历到的结点 , 用pre表示下一个结点 , 用nxt表示cur的下一个…

Windows 编译 yangfengzzz/fluid-engine-OpenVDB

我想将 OpenVDB 接入 doyubkim 的流体引擎 https://github.com/doyubkim/fluid-engine-dev 然后搜到已经有人做过这件事了 https://github.com/yangfengzzz/fluid-engine-OpenVDB Windows 编译 yangfengzzz/fluid-engine-OpenVDB 但是我是 windows,所以想要编译…

代码随想录算法训练营第三二天 | 买卖股票、跳跃游戏

目录 买卖股票的最佳时机II跳跃游戏跳跃游戏ii LeetCode 122.买卖股票的最佳时机II LeetCode 55. 跳跃游戏 LeetCode 45.跳跃游戏II 买卖股票的最佳时机II 只有一只股票! 当前只有买股票或者卖股票的操作。 最终利润是可以分解的:把利润分解为每天为…

【机器学习】机器学习常见算法详解第4篇:KNN算法计算过程(已分享,附代码)

本系列文章md笔记(已分享)主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习,伴随浅显易懂的数学知识,让大家掌握机器学习常见算法原理,应用Scikit-learn实现机器学习算法的应用&#xff0…

Vue练习1:组件开发1(头像组件)

样式预览 注释代码 <template><div class"img-box":style"{ //动态style必须为对象width: size rem,height: size rem}"><imgclass"avatar-img":src"url" //动态url/></div> </templ…

【ARM架构】ARMv8-A 系统中的安全架构概述

一个安全或可信的操作系统保护着系统中敏感的信息&#xff0c;例如&#xff0c;可以保护用户存储的密码&#xff0c;信用卡等认证信息免受攻击。 安全由以下原则定义&#xff1a; 保密性&#xff1a;保护设备上的敏感信息&#xff0c;防止未经授权的访问。有以下几种方法可以做…

算法详解:滑动窗口-- 最大连续1的个数 III

题目来源:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 本期讲解滑动窗口经典例题,我会从三个点开始讲解题目1.题目解析2.算法原理 3.编写代码 1.题目解析 这道题目理解起来还是比较简单的,我们简单分析一下,也就是给定一个数组,数组是由1和0组成…

PLC-Recorder的延伸分析功能说明

目录 一、缘起 二、如何从PLC-Recorder获取数据 1、在线获取 2、全自主打开数据文件 3、延伸分析 三、设置方法 四、效果展示 一、缘起 在各个行业&#xff0c;在不同的场景中&#xff0c;朋友们拿到数据后&#xff0c;想做的事情五花八门&#xff0c;有做宏观分析的、…

ABC341 A-G

Toyota Programming Contest 2024#2&#xff08;AtCoder Beginner Contest 341&#xff09; - AtCoder B读不懂题卡了&#xff0c;F读假题卡了&#xff0c;开题开慢了rank了 A - Print 341 题意&#xff1a; 打印一串交替出现的包含N个0&#xff0c;N1个1的01串 代码&…

OpenCV-40 绘制直方图

一、使用matplotlib画直方图 可以利用matplotlib把OpenCV统计得到的直方图绘制出来 示例代码如下&#xff1a; import cv2 import matplotlib.pyplot as pltlena cv2.imread("beautiful women.png") # 变为黑白图片 gray cv2.cvtColor(lena, cv2.COLOR_BGR2GRAY…

视觉设计师的项目评审复盘攻略:如何提升设计质量与效率

视觉设计师的角色是至关重要的&#xff0c;以确保设计项目满足预期的质量和结果。作为一名视觉设计师&#xff0c;有必要进行定期的项目审查&#xff0c;以确保项目在正轨上进行&#xff0c;并尽早解决任何问题。在本文中我们将讨论可视化设计人员如何做好项目评审&#xff0c;…

HarmonyOS—@State装饰器:组件内状态

State装饰的变量&#xff0c;或称为状态变量&#xff0c;一旦变量拥有了状态属性&#xff0c;就和自定义组件的渲染绑定起来。当状态改变时&#xff0c;UI会发生对应的渲染改变。 在状态变量相关装饰器中&#xff0c;State是最基础的&#xff0c;使变量拥有状态属性的装饰器&a…

永久禁止windows自动更新方法

文章目录 前言一、打开本地组策略编辑器二、禁用windows更新总结 前言 每次打开电脑&#xff0c;右下角就会弹出设备更新提示&#xff0c;看着令人烦恼&#xff0c;并且更新可能导致电脑设置发生改变甚至是卡顿&#xff0c;所以为了自己方便于是出了禁用电脑更新的办法&#x…

「算法」二分查找1:理论细节

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;算法详解 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 二分查找算法简介 这个算法的特点就是&#xff1a;细节多&#xff0c;出错率高&#xff0c;很容易就写成死循环有模板&#xff0c;但…

Day46 300最长递增子序列 674最长连续递增子序列 718最长重复子数组 1143最长公共子序列

300 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序…

CV | Segment Anything论文详解及代码实现

本文主要是详解解释了SAM的论文部分以及代码实现~ 论文&#xff1a;2023.04.05_Segment Anything 论文地址&#xff1a;2304.02643.pdf (arxiv.org) 代码地址&#xff1a;facebookresearch/segment-anything: The repository provides code for running inference with the Seg…

【C语言】长篇详解,字符系列篇1-----“混杂”的各种字符类型字符转换和strlen的模拟实现【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本期系列为【C语言】长篇详解&#xff0c;字符系列篇1-----“混杂”的各种字符函数……&#xff0c;图文讲解各种字符函数&#xff0c;带大家更深刻理解C语言中各种字符函数的应用&#xff0c;感谢观看&#xff0c;支持的可以给个赞哇。 前言…