
gorm初体验
数据库表
sql
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`age` tinyint(3) unsigned NOT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `articles` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` text COLLATE utf8mb4_unicode_ci,
`user_id` bigint(20) unsigned NOT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_articles_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
mock数据
sql
INSERT INTO `test`.`users`(`id`, `name`, `email`, `age`, `created_at`, `updated_at`) VALUES (1, '张三', 'zhangsan@example.com', 25, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`users`(`id`, `name`, `email`, `age`, `created_at`, `updated_at`) VALUES (2, '李四', 'lisi@example.com', 30, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`users`(`id`, `name`, `email`, `age`, `created_at`, `updated_at`) VALUES (3, '赵六', 'zhaoliu@example.com', 35, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`articles`(`id`, `title`, `content`, `user_id`, `created_at`, `updated_at`) VALUES (1, 'GORM使用指南', '这是一篇关于GORM的详细教程...', 1, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`articles`(`id`, `title`, `content`, `user_id`, `created_at`, `updated_at`) VALUES (2, 'Go语言入门', '学习Go语言的基础知识...', 1, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`articles`(`id`, `title`, `content`, `user_id`, `created_at`, `updated_at`) VALUES (3, 'Web开发实战', '使用Go进行Web开发...', 2, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
INSERT INTO `test`.`articles`(`id`, `title`, `content`, `user_id`, `created_at`, `updated_at`) VALUES (4, '数据库设计原则', '良好的数据库设计实践...', 3, '2025-09-16 12:53:22', '2025-09-16 12:53:22');
插入数据
go
package main
import (
"fmt"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 定义用户模型
type User struct {
ID uint `gorm:"primaryKey"` // 主键
Name string `gorm:"size:100;not null"` // 用户名,长度限制100,非空
Email string `gorm:"size:100;uniqueIndex"` // 邮箱,长度限制100,唯一索引
Age uint8 `gorm:"check:age > 0"` // 年龄,检查约束
Articles []Article `gorm:"foreignKey:UserID"` // 用户拥有的文章列表
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
// 定义文章模型(与用户是一对多关系)
type Article struct {
ID uint `gorm:"primaryKey"` // 主键
Title string `gorm:"size:200;not null"` // 文章标题
Content string `gorm:"type:text"` // 文章内容,使用文本类型
UserID uint `gorm:"not null"` // 外键,关联用户ID
User User `gorm:"foreignKey:UserID"` // 属于用户关系
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
func main() {
// MySQL 连接配置
// 格式: "user:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
// 连接数据库
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 设置日志级别为Info,显示所有SQL
})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// 获取底层的 sql.DB 对象并设置连接池
sqlDB, err := db.DB()
if err != nil {
log.Fatal("Failed to get database instance:", err)
}
// 设置连接池参数
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接的最大可复用时间
// 插入Mock数据
insertMockData(db)
// 关闭数据库连接
if err := sqlDB.Close(); err != nil {
log.Fatal("Failed to close database:", err)
}
}
// 插入Mock数据
func insertMockData(db *gorm.DB) {
// 先清空表
db.Exec("DELETE FROM articles")
db.Exec("DELETE FROM users")
// 创建用户
users := []User{
{Name: "张三", Email: "zhangsan@example.com", Age: 25},
{Name: "李四", Email: "lisi@example.com", Age: 30},
{Name: "赵六", Email: "zhaoliu@example.com", Age: 35},
}
for i := range users {
result := db.Create(&users[i])
if result.Error != nil {
log.Fatal("Failed to create user:", result.Error)
}
}
// 创建文章
articles := []Article{
{Title: "GORM使用指南", Content: "这是一篇关于GORM的详细教程...", UserID: users[0].ID},
{Title: "Go语言入门", Content: "学习Go语言的基础知识...", UserID: users[0].ID},
{Title: "Web开发实战", Content: "使用Go进行Web开发...", UserID: users[1].ID},
{Title: "数据库设计原则", Content: "良好的数据库设计实践...", UserID: users[2].ID},
}
for i := range articles {
result := db.Create(&articles[i])
if result.Error != nil {
log.Fatal("Failed to create article:", result.Error)
}
}
fmt.Println("Mock数据插入完成")
}
查询数据
go
package main
import (
"fmt"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 定义用户模型
type User struct {
ID uint `gorm:"primaryKey"` // 主键
Name string `gorm:"size:100;not null"` // 用户名,长度限制100,非空
Email string `gorm:"size:100;uniqueIndex"` // 邮箱,长度限制100,唯一索引
Age uint8 `gorm:"check:age > 0"` // 年龄,检查约束
Articles []Article `gorm:"foreignKey:UserID"` // 用户拥有的文章列表
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
// 定义文章模型(与用户是一对多关系)
type Article struct {
ID uint `gorm:"primaryKey"` // 主键
Title string `gorm:"size:200;not null"` // 文章标题
Content string `gorm:"type:text"` // 文章内容,使用文本类型
UserID uint `gorm:"not null"` // 外键,关联用户ID
User User `gorm:"foreignKey:UserID"` // 属于用户关系
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
func main() {
// MySQL 连接配置
// 格式: "user:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
// 连接数据库
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 设置日志级别为Info,显示所有SQL
})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// 获取底层的 sql.DB 对象并设置连接池
sqlDB, err := db.DB()
if err != nil {
log.Fatal("Failed to get database instance:", err)
}
// 设置连接池参数
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接的最大可复用时间
// 示例:查询所有用户及其文章
fmt.Println("=== 所有用户及其文章 ===")
var users []User
db.Preload("Articles").Find(&users) // Preload预加载关联的文章
for _, user := range users {
fmt.Printf("用户: %s (%d岁), 邮箱: %s\n", user.Name, user.Age, user.Email)
for _, article := range user.Articles {
fmt.Printf(" - 文章: 《%s》\n", article.Title)
}
}
// 关闭数据库连接
if err := sqlDB.Close(); err != nil {
log.Fatal("Failed to close database:", err)
}
}
where 条件查询
go
// 示例:条件查询
fmt.Println("\n=== 年龄大于25岁的用户 ===")
var olderUsers []User
db.Where("age > ?", 25).Find(&olderUsers)
for _, user := range olderUsers {
fmt.Printf("用户: %s (%d岁)\n", user.Name, user.Age)
}
update 更新数据
go
// 示例:更新用户信息
fmt.Println("\n=== 更新用户信息 ===")
// 先查询用户
var userToUpdate User
db.First(&userToUpdate, "email = ?", "lisi@example.com")
// 更新年龄
db.Model(&userToUpdate).Update("age", 31)
fmt.Printf("用户 %s 年龄更新为: %d\n", userToUpdate.Name, userToUpdate.Age)
事务操作
go
// 示例:事务操作 - 创建用户同时创建文章
fmt.Println("\n=== 事务操作 - 创建用户同时创建文章 ===")
err = db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些数据库操作
newUserWithArticle := User{
Name: "钱七",
Email: "qianqi@example.com",
Age: 32,
}
if err := tx.Create(&newUserWithArticle).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
newArticle := Article{
Title: "事务操作示例",
Content: "这是一个演示事务操作的示例文章...",
UserID: newUserWithArticle.ID,
}
if err := tx.Create(&newArticle).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
if err != nil {
log.Fatal("Transaction failed:", err)
}
fmt.Println("事务操作成功完成")
删除用户
go
// 示例:删除用户(物理删除)
fmt.Println("\n=== 删除用户 ===")
// 先查询用户
var userToDelete User
result := db.First(&userToDelete, "email = ?", "lisi@example.com")
// 检查查询结果
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Printf("未找到邮箱为 %s 的用户,无需删除\n", "zhangsan@example.com")
} else {
log.Fatal("查询用户时出错:", result.Error)
}
} else {
// 打印查询到的用户信息
fmt.Printf("找到用户: %s (ID: %d, 年龄: %d)\n",
userToDelete.Name, userToDelete.ID, userToDelete.Age)
result2 := db.Delete(&userToDelete)
if result2.Error != nil {
log.Fatal("Failed to delete user:", result2.Error)
}
fmt.Printf("用户 %s 已删除,影响行数: %d\n", userToDelete.Name, result2.RowsAffected)
}
原生查询
类似mybatis那种的结果映射
单对象映射
go
package main
import (
"errors"
"fmt"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 定义用户模型
type User struct {
ID uint `gorm:"primaryKey"` // 主键
Name string `gorm:"size:100;not null"` // 用户名,长度限制100,非空
Email string `gorm:"size:100;uniqueIndex"` // 邮箱,长度限制100,唯一索引
Age uint8 `gorm:"check:age > 0"` // 年龄,检查约束
Articles []Article `gorm:"foreignKey:UserID"` // 用户拥有的文章列表
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
// 定义文章模型(与用户是一对多关系)
type Article struct {
ID uint `gorm:"primaryKey"` // 主键
Title string `gorm:"size:200;not null"` // 文章标题
Content string `gorm:"type:text"` // 文章内容,使用文本类型
UserID uint `gorm:"not null"` // 外键,关联用户ID
User User `gorm:"foreignKey:UserID"` // 属于用户关系
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
}
// 定义一个结构体来映射查询结果
type UserResult struct {
Id uint `gorm:"column:id"` // 使用标签指定列名
Name string `gorm:"column:name"`
Email string `gorm:"column:email"`
Age int `gorm:"column:age"`
CreatedAt time.Time `gorm:"column:created_at"`
}
func main() {
// MySQL 连接配置
// 格式: "user:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
// 连接数据库
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 设置日志级别为Info,显示所有SQL
})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// 获取底层的 sql.DB 对象并设置连接池
sqlDB, err := db.DB()
if err != nil {
log.Fatal("Failed to get database instance:", err)
}
// 设置连接池参数
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接的最大可复用时间
// 示例 1: 查询单个用户信息
var user UserResult
userId := 3
// 使用 Raw 执行原生 SQL 查询,并通过 Scan 将结果映射到结构体
result := db.Raw("SELECT id, name, email, age, created_at FROM users WHERE id = ?", userId).Scan(&user)
if result.Error != nil {
// 检查是否是"记录未找到"的错误
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Printf("未找到ID为 %d 的用户\n", userId)
} else {
log.Fatal("查询执行失败:", result.Error)
}
} else {
fmt.Printf("用户信息: %+v\n", user)
}
}
多对象映射
go
// 示例: 查询多个用户信息
var users []UserResult
userId := 2
// 使用 Raw 执行原生 SQL 查询,并通过 Scan 将结果映射到结构体
result := db.Raw("SELECT id, name, email, age, created_at FROM users WHERE id >= ?", userId).Scan(&users)
if result.Error != nil {
log.Fatal("查询执行失败:", result.Error)
} else {
for _, user := range users {
fmt.Printf("用户信息: %+v\n", user)
}
}
更新删除操作
go
// 示例 5: 执行更新操作并获取影响行数
newAge := 30
updateResult := db.Exec("UPDATE users SET age = ? WHERE age < ?", newAge, 25)
if updateResult.Error != nil {
log.Fatal("Failed to execute update:", updateResult.Error)
}
rowsAffected := updateResult.RowsAffected
fmt.Printf("更新了 %d 行数据\n", rowsAffected)