Go语言数据库操作与ORM实践
一、数据库基础
Go语言提供了database/sql包作为数据库操作的标准接口,支持多种数据库驱动。
常用数据库驱动
| 数据库 | 驱动包 |
|---|---|
| MySQL | github.com/go-sql-driver/mysql |
| PostgreSQL | github.com/lib/pq |
| SQLite | github.com/mattn/go-sqlite3 |
| SQL Server | github.com/denisenkom/go-mssqldb |
基础连接
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func main() { // 连接字符串格式: user:password@tcp(host:port)/database dsn := "root:password@tcp(localhost:3306)/example_db" db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("Failed to open database: %v\n", err) return } defer db.Close() // 验证连接 err = db.Ping() if err != nil { fmt.Printf("Failed to ping database: %v\n", err) return } fmt.Println("Database connection successful") }二、CRUD操作
查询数据
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { ID int Name string Age int } func main() { db, err := sql.Open("mysql", "root:password@tcp(localhost:3306)/example_db") if err != nil { panic(err) } defer db.Close() // 单行查询 var user User err = db.QueryRow("SELECT id, name, age FROM users WHERE id = ?", 1).Scan(&user.ID, &user.Name, &user.Age) if err != nil { if err == sql.ErrNoRows { fmt.Println("User not found") } else { fmt.Printf("Query failed: %v\n", err) } return } fmt.Printf("User: %+v\n", user) // 多行查询 rows, err := db.Query("SELECT id, name, age FROM users WHERE age > ?", 18) if err != nil { fmt.Printf("Query failed: %v\n", err) return } defer rows.Close() var users []User for rows.Next() { var u User err = rows.Scan(&u.ID, &u.Name, &u.Age) if err != nil { fmt.Printf("Scan failed: %v\n", err) return } users = append(users, u) } fmt.Printf("Users: %+v\n", users) }插入数据
func createUser(db *sql.DB, name string, age int) (int64, error) { result, err := db.Exec("INSERT INTO users (name, age) VALUES (?, ?)", name, age) if err != nil { return 0, err } // 获取插入的ID id, err := result.LastInsertId() if err != nil { return 0, err } return id, nil }更新数据
func updateUser(db *sql.DB, id int, name string, age int) (int64, error) { result, err := db.Exec("UPDATE users SET name = ?, age = ? WHERE id = ?", name, age, id) if err != nil { return 0, err } // 获取影响的行数 rowsAffected, err := result.RowsAffected() if err != nil { return 0, err } return rowsAffected, nil }删除数据
func deleteUser(db *sql.DB, id int) (int64, error) { result, err := db.Exec("DELETE FROM users WHERE id = ?", id) if err != nil { return 0, err } rowsAffected, err := result.RowsAffected() if err != nil { return 0, err } return rowsAffected, nil }三、ORM框架
GORM基础
go get gorm.io/gorm go get gorm.io/driver/mysqlpackage main import ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) type User struct { gorm.Model Name string Age int } func main() { dsn := "root:password@tcp(localhost:3306)/example_db?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } // 自动迁移表结构 db.AutoMigrate(&User{}) // 创建记录 user := User{Name: "John", Age: 30} result := db.Create(&user) fmt.Printf("Created user with ID: %d, Error: %v\n", user.ID, result.Error) // 查询记录 var foundUser User db.First(&foundUser, 1) // 根据主键查询 fmt.Printf("Found user: %+v\n", foundUser) // 更新记录 db.Model(&foundUser).Update("Age", 31) // 删除记录 db.Delete(&foundUser) }GORM查询
// 条件查询 var users []User db.Where("age > ?", 18).Find(&users) // 链式查询 db.Where("name LIKE ?", "%John%").Order("age DESC").Limit(10).Find(&users) // 关联查询 type Order struct { gorm.Model UserID uint Amount float64 } var orders []Order db.Preload("User").Find(&orders)GORM事务
func transfer(db *gorm.DB, fromID, toID uint, amount float64) error { return db.Transaction(func(tx *gorm.DB) error { // 扣减余额 if err := tx.Model(&User{}).Where("id = ?", fromID).Update("balance", gorm.Expr("balance - ?", amount)).Error; err != nil { return err } // 增加余额 if err := tx.Model(&User{}).Where("id = ?", toID).Update("balance", gorm.Expr("balance + ?", amount)).Error; err != nil { return err } // 返回nil提交事务 return nil }) }四、数据库连接池
连接池配置
package main import ( "database/sql" "time" _ "github.com/go-sql-driver/mysql" ) func main() { db, err := sql.Open("mysql", "root:password@tcp(localhost:3306)/example_db") if err != nil { panic(err) } // 设置连接池参数 db.SetMaxOpenConns(100) // 最大打开连接数 db.SetMaxIdleConns(10) // 最大空闲连接数 db.SetConnMaxLifetime(30 * time.Minute) // 连接最大存活时间 db.SetConnMaxIdleTime(10 * time.Minute) // 连接最大空闲时间 defer db.Close() }连接池监控
func monitorPool(db *sql.DB) { stats := db.Stats() fmt.Printf("Open connections: %d\n", stats.OpenConnections) fmt.Printf("Idle connections: %d\n", stats.Idle) fmt.Printf("Connections used: %d\n", stats.InUse) fmt.Printf("Wait count: %d\n", stats.WaitCount) fmt.Printf("Wait duration: %v\n", stats.WaitDuration) }五、数据库迁移
使用go-migrate
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest创建迁移文件:
migrate create -ext sql -dir migrations -seq create_users_table迁移文件示例:
-- migrations/000001_create_users_table.up.sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, age INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- migrations/000001_create_users_table.down.sql DROP TABLE users;运行迁移:
migrate -path migrations -database "mysql://root:password@tcp(localhost:3306)/example_db" up六、数据库事务
原生事务
func doTransaction(db *sql.DB) error { tx, err := db.Begin() if err != nil { return err } defer func() { if r := recover(); r != nil { tx.Rollback() } }() // 执行SQL语句 _, err = tx.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 25) if err != nil { tx.Rollback() return err } _, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE user_id = ?", 1) if err != nil { tx.Rollback() return err } return tx.Commit() }Savepoint
func withSavepoint(db *sql.DB) error { tx, err := db.Begin() if err != nil { return err } // 设置savepoint _, err = tx.Exec("SAVEPOINT sp1") if err != nil { tx.Rollback() return err } // 执行操作 _, err = tx.Exec("INSERT INTO users (name) VALUES (?)", "Test") if err != nil { tx.Exec("ROLLBACK TO SAVEPOINT sp1") // 继续其他操作... } return tx.Commit() }七、SQL注入防护
参数化查询
// 安全:使用参数化查询 func safeQuery(db *sql.DB, username string) ([]User, error) { rows, err := db.Query("SELECT * FROM users WHERE username = ?", username) if err != nil { return nil, err } defer rows.Close() var users []User for rows.Next() { var u User err = rows.Scan(&u.ID, &u.Name) if err != nil { return nil, err } users = append(users, u) } return users, nil } // 不安全:字符串拼接 func unsafeQuery(db *sql.DB, username string) ([]User, error) { // 危险!可能导致SQL注入 query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username) rows, err := db.Query(query) // ... }ORM自动防护
// GORM自动使用参数化查询 func gormQuery(db *gorm.DB, username string) ([]User, error) { var users []User // GORM自动处理参数化 err := db.Where("username = ?", username).Find(&users).Error return users, err }八、数据库优化
查询优化
// 添加索引 db.Exec("CREATE INDEX idx_users_age ON users(age)") // 使用覆盖索引 rows, err := db.Query("SELECT id, name FROM users USE INDEX (idx_users_age) WHERE age > ?", 18) // 分页查询 rows, err := db.Query("SELECT * FROM users LIMIT ? OFFSET ?", limit, offset)批量操作
// 批量插入 func batchInsert(db *sql.DB, users []User) error { tx, err := db.Begin() if err != nil { return err } stmt, err := tx.Prepare("INSERT INTO users (name, age) VALUES (?, ?)") if err != nil { return err } defer stmt.Close() for _, user := range users { _, err = stmt.Exec(user.Name, user.Age) if err != nil { tx.Rollback() return err } } return tx.Commit() }九、总结
Go语言的数据库操作通过database/sql包提供了统一的接口,结合ORM框架可以大大提高开发效率。以下是关键要点:
- 数据库连接:使用
sql.Open创建连接,配置连接池参数 - CRUD操作:使用参数化查询防止SQL注入
- ORM框架:GORM提供便捷的数据访问层
- 事务处理:使用
db.Begin()开启事务 - 数据库迁移:使用go-migrate管理schema变更
- 性能优化:索引优化、批量操作、查询优化
合理使用数据库操作可以显著提升应用的性能和可靠性。