《Go 简易速速上手小册》第9章:数据库交互(2024 最新版)(上)+https://developer.aliyun.com/article/1487013
9.2.3 拓展案例 1:图书借阅记录
在这个案例中,我们将扩展图书管理系统,添加功能以记录用户的图书借阅信息。这项功能涉及到创建新的数据库表来存储借阅记录,并实现相关的增、查操作。
准备数据库
首先,除了已有的books
表之外,我们还需要创建两个新的表:users
和borrow_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和数据库操作的深入掌握,你将能够构建更加复杂和功能丰富的应用。