feat(sidebar): 添加 Web开发数据库 侧边栏模块
- 在侧边栏配置中新增 Web开发数据库分类 - 增加相关文档链接,包括 Gin 框架、GORM、Viper、Zap 等内容 - 设置分类可折叠,提升导航体验 refactor(go): 精简 Go 示例代码导入包 - 移除未使用的 runtime 和 time 包 - 保留 fmt 和 sync 包,优化依赖 docs(linux): 删除 Nginx 基础入门文档 - 移除包含 Nginx 基本概念、安装、配置和常用命令的完整文档 - 清理废弃内容,简化项目文档结构
This commit is contained in:
810
src/programming/backend/go/Web开发数据库/17GORM数据库.md
Normal file
810
src/programming/backend/go/Web开发数据库/17GORM数据库.md
Normal file
@@ -0,0 +1,810 @@
|
||||
---
|
||||
title: GORM数据库
|
||||
icon: mdi:database
|
||||
date: 2025-12-23
|
||||
category:
|
||||
- Go
|
||||
- 后端
|
||||
- 数据库
|
||||
- ORM
|
||||
- GORM
|
||||
tag:
|
||||
- GORM
|
||||
- SQLite
|
||||
- MySQL
|
||||
- PostgreSQL
|
||||
- CRUD
|
||||
---
|
||||
|
||||
GORM 是 Go 最流行的 ORM 库,提供完善的数据库操作能力。本文通过完整可运行的示例,并结合 JPA/Hibernate 对比,帮助你快速掌握 GORM 使用。
|
||||
|
||||
<!-- more -->
|
||||
|
||||
---
|
||||
|
||||
# GORM 数据库操作:模型定义、CRUD、查询
|
||||
|
||||
> **Java 对比**:GORM 类似 JPA/Hibernate,但更轻量且代码更简洁。
|
||||
|
||||
---
|
||||
|
||||
## 一、项目初始化
|
||||
|
||||
### 1.1 创建项目
|
||||
|
||||
```bash
|
||||
cd ~/GolandProjects
|
||||
mkdir go-gorm-demo && cd go-gorm-demo
|
||||
go mod init go-gorm-demo
|
||||
|
||||
# 安装 GORM 和驱动(指定兼容版本)
|
||||
go get gorm.io/gorm@v1.25.5
|
||||
go get gorm.io/driver/sqlite@v1.5.4 # SQLite
|
||||
go get gorm.io/driver/mysql@v1.5.2 # MySQL(可选)
|
||||
go get gorm.io/driver/postgres@v1.5.4 # PostgreSQL(可选)
|
||||
```
|
||||
|
||||
> **版本说明**:
|
||||
> - 所有示例代码兼容 Go 1.18-1.22 版本
|
||||
> - GORM v1.25.5 已在 Go 1.22.2 上测试通过
|
||||
> - 如需使用最新版本:`go get -u gorm.io/gorm`
|
||||
|
||||
---
|
||||
|
||||
## 二、数据库连接
|
||||
|
||||
### 2.1 连接 SQLite
|
||||
|
||||
创建 `db_connect.go`:
|
||||
|
||||
```bash
|
||||
nano db_connect.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 连接 SQLite(文件数据库)
|
||||
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
|
||||
fmt.Println("Database connected successfully!")
|
||||
|
||||
// 获取底层的 *sql.DB
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 设置连接池
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
|
||||
fmt.Println("Connection pool configured")
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run db_connect.go
|
||||
# Database connected successfully!
|
||||
# Connection pool configured
|
||||
```
|
||||
|
||||
### 2.2 连接 MySQL(可选)
|
||||
|
||||
创建 `db_connect_mysql.go`:
|
||||
|
||||
```bash
|
||||
nano db_connect_mysql.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// MySQL DSN 格式: user:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local
|
||||
dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
|
||||
fmt.Println("MySQL connected successfully!")
|
||||
}
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - GORM 连接 ≈ JPA 的 `EntityManagerFactory`
|
||||
> - `gorm.Open()` ≈ Hibernate 的 `SessionFactory`
|
||||
|
||||
---
|
||||
|
||||
## 三、模型定义
|
||||
|
||||
### 3.1 基础模型
|
||||
|
||||
创建 `model_basic.go`:
|
||||
|
||||
```bash
|
||||
nano model_basic.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// User 模型
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100;not null"`
|
||||
Email string `gorm:"size:100;unique;not null"`
|
||||
Age int `gorm:"default:0"`
|
||||
Active bool `gorm:"default:true"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
|
||||
// 自动迁移(创建表)
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
fmt.Println("Table created successfully!")
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run model_basic.go
|
||||
# Table created successfully!
|
||||
```
|
||||
|
||||
### 3.2 常用 Tag 说明
|
||||
|
||||
| Tag | 说明 | Java/JPA 对比 |
|
||||
|-----|------|--------------|
|
||||
| `primaryKey` | 主键 | `@Id` |
|
||||
| `autoIncrement` | 自增 | `@GeneratedValue` |
|
||||
| `size:100` | 字段长度 | `@Column(length=100)` |
|
||||
| `unique` | 唯一约束 | `@Column(unique=true)` |
|
||||
| `not null` | 非空约束 | `@Column(nullable=false)` |
|
||||
| `default:value` | 默认值 | `@Column(columnDefinition="...")` |
|
||||
| `index` | 索引 | `@Index` |
|
||||
| `-` | 忽略字段 | `@Transient` |
|
||||
|
||||
> **Java 对比**:
|
||||
> - GORM Tag ≈ JPA 注解(`@Entity`, `@Column`)
|
||||
> - `AutoMigrate` ≈ Hibernate 的 `hbm2ddl.auto=update`
|
||||
|
||||
---
|
||||
|
||||
## 四、CRUD 操作
|
||||
|
||||
### 4.1 Create - 创建数据
|
||||
|
||||
创建 `crud_create.go`:
|
||||
|
||||
```bash
|
||||
nano crud_create.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// 1. 单条插入
|
||||
user := User{
|
||||
Name: "Alice",
|
||||
Email: "alice@example.com",
|
||||
Age: 30,
|
||||
}
|
||||
result := db.Create(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
panic(result.Error)
|
||||
}
|
||||
|
||||
fmt.Printf("New user ID: %d\n", user.ID)
|
||||
fmt.Printf("Rows affected: %d\n", result.RowsAffected)
|
||||
|
||||
// 2. 批量插入
|
||||
users := []User{
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 25},
|
||||
{Name: "Charlie", Email: "charlie@example.com", Age: 35},
|
||||
{Name: "David", Email: "david@example.com", Age: 28},
|
||||
}
|
||||
|
||||
db.Create(users)
|
||||
fmt.Println("Batch insert completed!")
|
||||
|
||||
// 3. 指定字段插入
|
||||
user2 := User{Name: "Eve", Email: "eve@example.com", Age: 22}
|
||||
db.Select("name", "email").Create(&user2) // 仅插入 name 和 email
|
||||
|
||||
fmt.Println("All users created successfully!")
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run crud_create.go
|
||||
# New user ID: 1
|
||||
# Rows affected: 1
|
||||
# Batch insert completed!
|
||||
# All users created successfully!
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - `db.Create()` ≈ `entityManager.persist()`
|
||||
> - 批量插入 ≈ `saveAll()`
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Read - 查询数据
|
||||
|
||||
创建 `crud_read.go`:
|
||||
|
||||
```bash
|
||||
nano crud_read.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
|
||||
// 先插入测试数据
|
||||
db.AutoMigrate(&User{})
|
||||
db.Create(&User{Name: "Alice", Email: "alice@example.com", Age: 30})
|
||||
db.Create(&User{Name: "Bob", Email: "bob@example.com", Age: 25})
|
||||
db.Create(&User{Name: "Charlie", Email: "charlie@example.com", Age: 35})
|
||||
|
||||
// 1. 根据 ID 查询
|
||||
var user User
|
||||
db.First(&user, 1) // 查询 ID=1 的用户
|
||||
fmt.Printf("User ID=1: %s (%s)\n", user.Name, user.Email)
|
||||
|
||||
// 2. 按条件查询单条
|
||||
var user2 User
|
||||
db.Where("email = ?", "bob@example.com").First(&user2)
|
||||
fmt.Printf("User by email: %s (age=%d)\n", user2.Name, user2.Age)
|
||||
|
||||
// 3. 查询所有
|
||||
var users []User
|
||||
db.Find(&users)
|
||||
fmt.Printf("Total users: %d\n", len(users))
|
||||
|
||||
// 4. 按条件查询多条
|
||||
var adults []User
|
||||
db.Where("age >= ?", 30).Find(&adults)
|
||||
fmt.Printf("Users age >= 30: %d\n", len(adults))
|
||||
|
||||
// 5. 使用 IN 查询
|
||||
var selectedUsers []User
|
||||
db.Where("id IN ?", []int{1, 2}).Find(&selectedUsers)
|
||||
fmt.Printf("Selected users: %d\n", len(selectedUsers))
|
||||
|
||||
// 6. 排序查询
|
||||
var sortedUsers []User
|
||||
db.Order("age DESC").Find(&sortedUsers)
|
||||
fmt.Println("Users sorted by age (DESC):")
|
||||
for _, u := range sortedUsers {
|
||||
fmt.Printf(" - %s (age=%d)\n", u.Name, u.Age)
|
||||
}
|
||||
|
||||
// 7. 分页查询
|
||||
var pageUsers []User
|
||||
db.Offset(0).Limit(2).Find(&pageUsers)
|
||||
fmt.Printf("Page 1 (limit 2): %d users\n", len(pageUsers))
|
||||
|
||||
// 8. 组合查询
|
||||
var combinedUsers []User
|
||||
db.Where("age > ?", 25).
|
||||
Order("age DESC").
|
||||
Limit(2).
|
||||
Find(&combinedUsers)
|
||||
fmt.Printf("Combined query: %d users\n", len(combinedUsers))
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run crud_read.go
|
||||
# User ID=1: Alice (alice@example.com)
|
||||
# User by email: Bob (age=25)
|
||||
# Total users: 3
|
||||
# Users age >= 30: 2
|
||||
# Selected users: 2
|
||||
# Users sorted by age (DESC):
|
||||
# - Charlie (age=35)
|
||||
# - Alice (age=30)
|
||||
# - Bob (age=25)
|
||||
# Page 1 (limit 2): 2 users
|
||||
# Combined query: 2 users
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - `db.Find()` ≈ `findAll()`
|
||||
> - `db.Where()` ≈ `@Query("WHERE ...")`
|
||||
> - `db.Order()` ≈ `Sort.by()`
|
||||
> - `db.Limit()` ≈ `Pageable.ofSize()`
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Update - 更新数据
|
||||
|
||||
创建 `crud_update.go`:
|
||||
|
||||
```bash
|
||||
nano crud_update.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// 插入测试数据
|
||||
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
|
||||
db.Create(&user)
|
||||
|
||||
// 1. 更新单个字段
|
||||
db.Model(&User{}).Where("id = ?", user.ID).Update("name", "Alice Updated")
|
||||
fmt.Println("Updated single field")
|
||||
|
||||
// 2. 更新多个字段(使用 map)
|
||||
db.Model(&User{}).Where("id = ?", user.ID).Updates(map[string]interface{}{
|
||||
"name": "Alice Smith",
|
||||
"age": 31,
|
||||
})
|
||||
fmt.Println("Updated multiple fields (map)")
|
||||
|
||||
// 3. 更新多个字段(使用 struct,仅更新非零值)
|
||||
db.Model(&User{}).Where("id = ?", user.ID).Updates(User{
|
||||
Name: "Alice Johnson",
|
||||
Age: 32,
|
||||
})
|
||||
fmt.Println("Updated multiple fields (struct)")
|
||||
|
||||
// 4. 强制更新零值字段
|
||||
db.Model(&User{}).Where("id = ?", user.ID).Update("age", 0)
|
||||
fmt.Println("Updated age to zero")
|
||||
|
||||
// 5. 批量更新
|
||||
db.Model(&User{}).Where("age > ?", 25).Update("age", gorm.Expr("age + ?", 1))
|
||||
fmt.Println("Batch update completed")
|
||||
|
||||
// 查询最终结果
|
||||
var finalUser User
|
||||
db.First(&finalUser, user.ID)
|
||||
fmt.Printf("Final user: %s (age=%d)\n", finalUser.Name, finalUser.Age)
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run crud_update.go
|
||||
# Updated single field
|
||||
# Updated multiple fields (map)
|
||||
# Updated multiple fields (struct)
|
||||
# Updated age to zero
|
||||
# Batch update completed
|
||||
# Final user: Alice Johnson (age=0)
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - `db.Updates()` ≈ `entityManager.merge()`
|
||||
> - 批量更新 ≈ `@Modifying @Query("UPDATE ...")`
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Delete - 删除数据
|
||||
|
||||
创建 `crud_delete.go`:
|
||||
|
||||
```bash
|
||||
nano crud_delete.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"` // 软删除字段
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// 插入测试数据
|
||||
users := []User{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 30},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 25},
|
||||
{Name: "Charlie", Email: "charlie@example.com", Age: 35},
|
||||
}
|
||||
db.Create(users)
|
||||
|
||||
// 1. 软删除(标记 deleted_at)
|
||||
db.Delete(&User{}, 1) // 删除 ID=1 的用户
|
||||
fmt.Println("Soft deleted user ID=1")
|
||||
|
||||
// 2. 查询时默认不包括已删除的
|
||||
var activeUsers []User
|
||||
db.Find(&activeUsers)
|
||||
fmt.Printf("Active users: %d\n", len(activeUsers))
|
||||
|
||||
// 3. 查询所有记录(包括已删除的)
|
||||
var allUsers []User
|
||||
db.Unscoped().Find(&allUsers)
|
||||
fmt.Printf("All users (including deleted): %d\n", len(allUsers))
|
||||
|
||||
// 4. 永久删除(硬删除)
|
||||
db.Unscoped().Delete(&User{}, 2)
|
||||
fmt.Println("Permanently deleted user ID=2")
|
||||
|
||||
// 5. 批量删除
|
||||
db.Where("age < ?", 30).Delete(&User{})
|
||||
fmt.Println("Batch delete completed")
|
||||
|
||||
// 最终统计
|
||||
var finalCount int64
|
||||
db.Model(&User{}).Count(&finalCount)
|
||||
fmt.Printf("Final active users: %d\n", finalCount)
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run crud_delete.go
|
||||
# Soft deleted user ID=1
|
||||
# Active users: 2
|
||||
# All users (including deleted): 3
|
||||
# Permanently deleted user ID=2
|
||||
# Batch delete completed
|
||||
# Final active users: 1
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - 软删除 ≈ 使用 `@SQLDelete` + `@Where`
|
||||
> - `DeletedAt` ≈ JPA 的逻辑删除注解
|
||||
> - 硬删除 ≈ `entityManager.remove()`
|
||||
|
||||
---
|
||||
|
||||
## 五、高级查询
|
||||
|
||||
### 5.1 复杂查询
|
||||
|
||||
创建 `query_advanced.go`:
|
||||
|
||||
```bash
|
||||
nano query_advanced.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
Active bool `gorm:"default:true"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// 插入测试数据
|
||||
users := []User{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 30, Active: true},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 25, Active: false},
|
||||
{Name: "Charlie", Email: "charlie@example.com", Age: 35, Active: true},
|
||||
{Name: "David", Email: "david@example.com", Age: 28, Active: true},
|
||||
}
|
||||
db.Create(users)
|
||||
|
||||
// 1. 多条件查询
|
||||
var result1 []User
|
||||
db.Where("age > ? AND active = ?", 25, true).Find(&result1)
|
||||
fmt.Printf("Age > 25 AND active: %d users\n", len(result1))
|
||||
|
||||
// 2. OR 查询
|
||||
var result2 []User
|
||||
db.Where("age < ? OR active = ?", 30, false).Find(&result2)
|
||||
fmt.Printf("Age < 30 OR inactive: %d users\n", len(result2))
|
||||
|
||||
// 3. 模糊查询
|
||||
var result3 []User
|
||||
db.Where("name LIKE ?", "%li%").Find(&result3)
|
||||
fmt.Printf("Name contains 'li': %d users\n", len(result3))
|
||||
|
||||
// 4. 仅查询指定字段
|
||||
var result4 []User
|
||||
db.Select("name", "email").Find(&result4)
|
||||
fmt.Printf("Selected fields: %d users\n", len(result4))
|
||||
for _, u := range result4 {
|
||||
fmt.Printf(" - %s (%s)\n", u.Name, u.Email)
|
||||
}
|
||||
|
||||
// 5. 统计查询
|
||||
var count int64
|
||||
db.Model(&User{}).Where("active = ?", true).Count(&count)
|
||||
fmt.Printf("Active users count: %d\n", count)
|
||||
|
||||
// 6. 聚合查询
|
||||
var avgAge float64
|
||||
db.Model(&User{}).Select("AVG(age)").Row().Scan(&avgAge)
|
||||
fmt.Printf("Average age: %.2f\n", avgAge)
|
||||
|
||||
// 7. 分组查询
|
||||
type Result struct {
|
||||
Active bool
|
||||
Count int64
|
||||
}
|
||||
var groupResult []Result
|
||||
db.Model(&User{}).Select("active, count(*) as count").Group("active").Scan(&groupResult)
|
||||
fmt.Println("Group by active:")
|
||||
for _, r := range groupResult {
|
||||
fmt.Printf(" Active=%v: %d users\n", r.Active, r.Count)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run query_advanced.go
|
||||
# Age > 25 AND active: 2 users
|
||||
# Age < 30 OR inactive: 2 users
|
||||
# Name contains 'li': 2 users
|
||||
# Selected fields: 4 users
|
||||
# - Alice (alice@example.com)
|
||||
# - Bob (bob@example.com)
|
||||
# - Charlie (charlie@example.com)
|
||||
# - David (david@example.com)
|
||||
# Active users count: 3
|
||||
# Average age: 29.50
|
||||
# Group by active:
|
||||
# Active=false: 1 users
|
||||
# Active=true: 3 users
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - `db.Where()` ≈ JPA Criteria API 或 `@Query`
|
||||
> - `db.Count()` ≈ `countBy...`
|
||||
> - `db.Group()` ≈ JPQL 的 `GROUP BY`
|
||||
|
||||
---
|
||||
|
||||
## 六、原生 SQL
|
||||
|
||||
### 6.1 执行原生查询
|
||||
|
||||
创建 `raw_sql.go`:
|
||||
|
||||
```bash
|
||||
nano raw_sql.go
|
||||
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"size:100"`
|
||||
Email string `gorm:"size:100;unique"`
|
||||
Age int
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
|
||||
db.AutoMigrate(&User{})
|
||||
|
||||
// 插入测试数据
|
||||
db.Create(&User{Name: "Alice", Email: "alice@example.com", Age: 30})
|
||||
db.Create(&User{Name: "Bob", Email: "bob@example.com", Age: 25})
|
||||
|
||||
// 1. 原生查询
|
||||
var users []User
|
||||
db.Raw("SELECT * FROM users WHERE age > ?", 20).Scan(&users)
|
||||
fmt.Printf("Raw query result: %d users\n", len(users))
|
||||
|
||||
// 2. 原生更新
|
||||
db.Exec("UPDATE users SET age = age + 1 WHERE name = ?", "Alice")
|
||||
fmt.Println("Raw update executed")
|
||||
|
||||
// 3. 原生统计
|
||||
var count int64
|
||||
db.Raw("SELECT COUNT(*) FROM users").Scan(&count)
|
||||
fmt.Printf("Total users: %d\n", count)
|
||||
|
||||
// 4. 查询单行
|
||||
var result struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
db.Raw("SELECT name, age FROM users WHERE id = ?", 1).Scan(&result)
|
||||
fmt.Printf("User: %s (age=%d)\n", result.Name, result.Age)
|
||||
}
|
||||
```
|
||||
|
||||
### 运行 & 测试
|
||||
|
||||
```bash
|
||||
go run raw_sql.go
|
||||
# Raw query result: 2 users
|
||||
# Raw update executed
|
||||
# Total users: 2
|
||||
# User: Alice (age=31)
|
||||
```
|
||||
|
||||
> **Java 对比**:
|
||||
> - `db.Raw()` ≈ `entityManager.createNativeQuery()`
|
||||
> - `db.Exec()` ≈ `@Query(nativeQuery = true)`
|
||||
|
||||
---
|
||||
|
||||
## 七、对比总结
|
||||
|
||||
| 功能 | GORM | JPA/Hibernate | 说明 |
|
||||
|------|------|--------------|------|
|
||||
| 连接数据库 | `gorm.Open()` | `EntityManagerFactory` | GORM 更简洁 |
|
||||
| 模型定义 | Struct + Tag | `@Entity` + 注解 | GORM 更轻量 |
|
||||
| 创建 | `db.Create()` | `persist()` | 语法不同 |
|
||||
| 查询 | `db.Find()` | `findAll()` | GORM 更灵活 |
|
||||
| 更新 | `db.Updates()` | `merge()` | GORM 支持链式 |
|
||||
| 删除 | `db.Delete()` | `remove()` | GORM 自带软删除 |
|
||||
| 原生SQL | `db.Raw()` | `createNativeQuery()` | 类似 |
|
||||
|
||||
---
|
||||
|
||||
## 八、常见问题
|
||||
|
||||
**Q: GORM 如何防止 SQL 注入?**
|
||||
|
||||
A: GORM 使用预编译语句(prepared statements),只要使用 `?` 占位符就是安全的。
|
||||
|
||||
**Q: 软删除和硬删除的区别?**
|
||||
|
||||
A:
|
||||
- 软删除:仅标记 `deleted_at` 字段,数据仍在数据库中
|
||||
- 硬删除:使用 `Unscoped().Delete()` 永久删除数据
|
||||
|
||||
**Q: 如何处理查询不存在的记录?**
|
||||
|
||||
A:
|
||||
```go
|
||||
if result := db.First(&user, id); result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
// 记录不存在
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、下一步
|
||||
|
||||
✅ 已掌握 GORM 基础操作
|
||||
→ 下一章:**GORM 事务与关联**(一对多、多对多)
|
||||
→ 再下一章:**配置管理**(Viper)
|
||||
|
||||
祝你编码愉快!🚀
|
||||
Reference in New Issue
Block a user