快速学习gorm 框架

2024-08-22 15:12
文章标签 学习 快速 框架 gorm

本文主要是介绍快速学习gorm 框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

go-orm

介绍

godemo是一款go目前主流的orm框架

软件架构

官方文档 GORM - The fantastic ORM library for Golang, aims to be developer friendly.

使用说明

安装教程

1.设置代理

go env -w  GOPROXY=https://goproxy.cn,direct

2.打算使用gin 搭配gorom 进行学习模拟web开发

## 安装gin
go get -u github.com/gin-gonic/gin
#数据库驱动和orm
go get gorm.io/driver/mysql
go get gorm.io/gorm

连接

数据库连
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情dsn := "用户名:密码@tcp(127.0.0.1:3306)/数据库名?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

添加配置 自定义驱动


import (_ "example.com/my_mysql_driver""gorm.io/driver/mysql""gorm.io/gorm"
)db, err := gorm.Open(mysql.New(mysql.Config{DriverName: "my_mysql_driver",DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name, 详情参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
}), &gorm.Config{})
	// 日志配置var mysqlLogger logger.Interface// 要显示的日志等级mysqlLogger = logger.Default.LogMode(logger.Info)db, _ := gorm.Open(mysql.New(mysql.Config{// leave blank for default driverDSN: "root:111111@tcp(127.0.0.1:3306)/ruiji?charset=utf8&parseTime=True&loc=Local", // data source name, 详情参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name}), &gorm.Config{Logger:                 mysqlLogger, //注册SkipDefaultTransaction: true,        // 禁用默认事务})sqlDB, _ := db.DB()// 设置空闲连接池中的最大连接数。ol.sqlDB.SetMaxIdleConns(10)// 设置与数据库的最大打开连接数sqlDB.SetMaxOpenConns(100)// 设置可以重复使用连接的最长时间。sqlDB.SetConnMaxLifetime(time.Hour)

CRUD

假如有model


type Student struct {ID uint // 默认使用ID作为主键//设置字段长度为2Name  string  `gorm:"size:10"`Sex   string  `gorm:"size:2"`Email *string // 使用指针是为了存空值Age   int
}

gorm中的tag约束 除开上述 还有以下 可以做到创建表时候约束

创建表

如果存在也不会报错 如果结构体和数据库模型 不一样则执行的是alter 更改数据库的操作

使用 AutoMigrate 方法自动迁移你的 schema,这将会创建数据库中不存在的表。
db.AutoMigrate(&Product{})

插入数据

单挑数据插入

db.Create(s) s是该映射结构体的指针

func insertStudent(c *gin.Context) {s := new(Student)if err := c.ShouldBindQuery(s); err != nil {return} else {db.Create(s)c.JSON(200, gin.H{"data":    s,"message": "创建用户成功",})}
}

批量插入 同样 参数传递结构体切片即可

func bentchInserter(c *gin.Context) {students := make([]Student, 10)for i := 1; i <= 20; i++ {students = append(students, Student{Name: "测试" + strconv.Itoa(i),Age:  i,Sex:  "男",//Email: nil,})}db.Create(&students)c.JSON(200, gin.H{"data":    students,"message": "批量创建用户成功",})
}

插入后该指针结构体的id字段就会被填充

删除

api delete

//第一个参数 是映射结构体指针 第二个参数是id 只有空结构体指针时候代表删除全表 
db.Delete(s, id)

等效

s.ID = uint(parsedID)
db.Delete(s) 

Or where 拼接查询条件 可以使用?方式进行拼接sql

	db.Or("age<?", 18).Where("name", "测试张三").Delete(s, id) // 根据主键删除

日志

[4.352ms] [rows:1] DELETE FROM students WHERE name = ‘测试张三’ AND students.id = ‘1’

注意

gorm 对于不携带条件的批量修改字段 比如UPDATE students SET sex=‘妈妈’() ,DELETE FROM students 这俩个sql正常来说是会改变整个表的记录,但是gorom 并不会去执行

sql1 go中的api

s := new(Student)
db.Model(s).Update("sex", "妈妈")

sq2

db.Delete(s)

但是加一个简单的条件

db.Model(s).Where("1 = 1").Update("sex", "男")

[7.314ms] [rows:26] UPDATE students SET sex=‘男’ WHERE 1 = 1 日志输出却可以成功改变全表记录

GORM 中无条件批量操作的行为总结

在 GORM 中,某些批量操作(如 DeleteUpdate)具有防护机制,以防止意外更改整个数据库。以下是关键行为的总结:

  1. 无条件删除
    • 当你尝试在不指定条件的情况下删除记录时,GORM 可能会生成类似 DELETE FROM 'students' 的 SQL 语句,但实际上不会删除任何记录。输出会显示 rows:0,以防止意外的大规模删除操作。
  2. 无条件更新
    • 使用 Struct 和 Updates 当你使用 db.Model(&Student{}).Updates(Student{Sex: "未"}) 时,GORM 会尝试更新表中所有记录的字段。但它会执行检查,避免在没有条件的情况下进行大规模更新。如果没有指定条件,这种操作通常不会执行。
    • 使用 Update 更新单个字段: 如果你执行 db.Model(&Student{}).Update("sex", "未"),GORM 的日志会显示它计划更新所有记录的 sex 字段,但如果没有明确的条件,它仍不会执行。
  3. 使用虚拟条件的操作
    • 使用虚拟条件: 通过添加像 db.Model(s).Where("1 = 1").Update("sex", "妈妈") 这样的条件,更新操作会执行,因为存在条件(即使是一个简单的条件)。

关键提示

映射指针没有id就是全表操作 有id 还设置id查询条件就是and 拼接

修改

修改单个字段 update api

s := &Student{ID: uint(id)} // 创建 Student 结构体并设置 ID  无论批量还是单个更新值字段,都需要id 不然无法找到数据
//如果没有id 就是对全表操作 但是没有条件的全表操作不会执行db.Model(s).Update("sex", "未") 

修改多个字段 updates api

db.Model(s).Where("id = ?", id).Where("age > ?", 10).Updates(Student{Name: "测试张三", Age: 18, Sex: "男"}) // 根据主

批量修改全表,只需要一个空结构体映射指针和简单的条件

db.Model(s).Where("1 = 1").Updates(Student{Name: "测试张三", Age: 18, Sex: "男"})

查询

单记录

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error or nil// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

批量 find


// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

虽然go中有select来简写查询语句 但是我还是喜欢where拼接的方式

DB.Select("name", "age").Find(&users)

在 GORM 中,Scan 方法用于将查询结果扫描到 Go 变量中。它允许你执行原始 SQL 查询,并将查询结果填充到指定的结构体或切片中。Scan 方法通常与 Raw 方法结合使用,用于处理更复杂的 SQL 查询。

可以根据这个方法来写原生sql

db.Raw("SELECT * FROM students WHERE id = ?", 1).Scan(&student)
分组查询
DB.Table("students").Select("count(id)").Group("gender").Scan(&ageList)
分页查询
// 一页两条,第1页
DB.Limit(2).Offset(0).Find(&users)
fmt.Println(users)
// 第2页
DB.Limit(2).Offset(2).Find(&users)
fmt.Println(users)
// 第3页   pagesize*(页号-1)
DB.Limit(2).Offset(4).Find(&users)
fmt.Println(users)
多表联查

下面是之前示例的 GORM 代码,并在每个示例的注释中添加了对应的 SQL 转换。

1. 多表联查 (JOIN Queries)
var users []User
db.Joins("JOIN orders ON orders.user_id = users.id").Where("orders.status = ?", "completed").Find(&users)// SQL: SELECT * FROM users 
//      JOIN orders ON orders.user_id = users.id 
//      WHERE orders.status = 'completed';
2. 范围查询 (Range Queries)
var orders []Order
db.Where("created_at BETWEEN ? AND ?", startDate, endDate).Find(&orders)// SQL: SELECT * FROM orders 
//      WHERE created_at BETWEEN 'startDate' AND 'endDate';
3. 子查询 (Subqueries)
subQuery := db.Table("orders").Select("AVG(amount)").Where("user_id = ?", userID).SubQuery()var orders []Order
db.Where("amount > ?", subQuery).Find(&orders)// SQL: SELECT * FROM orders 
//      WHERE amount > (SELECT AVG(amount) FROM orders WHERE user_id = userID);
4. 多条件复杂查询 (Complex Queries with Multiple Conditions)
var users []User
db.Where("age BETWEEN ? AND ?", minAge, maxAge).Where("region = ?", region).Where("status = ?", "active").Find(&users)// SQL: SELECT * FROM users 
//      WHERE age BETWEEN minAge AND maxAge 
//      AND region = 'region' 
//      AND status = 'active';
5. 联合查询 (UNION Queries)
var results []User
db.Raw("SELECT * FROM users WHERE region = ? UNION SELECT * FROM users WHERE region = ?", region1, region2).Scan(&results)// SQL: SELECT * FROM users WHERE region = 'region1' 
//      UNION 
//      SELECT * FROM users WHERE region = 'region2';
6. 预加载和条件加载 (Preload with Conditions)
var users []User
db.Preload("Orders", "status = ?", "completed").Find(&users)// SQL: SELECT * FROM users; 
//      SELECT * FROM orders WHERE user_id IN (userIDs) AND status = 'completed';
7. 分组与聚合查询 (Group By and Aggregate Functions)
type Result struct {UserID uintCount  int
}var results []Result
db.Model(&Order{}).Select("user_id, COUNT(*) as count").Group("user_id").Having("count > ?", 5).Find(&results)// SQL: SELECT user_id, COUNT(*) as count 
//      FROM orders 
//      GROUP BY user_id 
//      HAVING count > 5;
8. 关联表条件查询 (Conditions on Related Tables)
var users []User
db.Joins("JOIN orders ON orders.user_id = users.id").Joins("JOIN order_items ON order_items.order_id = orders.id").Joins("JOIN products ON products.id = order_items.product_id").Where("products.name = ?", "Product A").Find(&users)// SQL: SELECT users.* FROM users 
//      JOIN orders ON orders.user_id = users.id 
//      JOIN order_items ON order_items.order_id = orders.id 
//      JOIN products ON products.id = order_items.product_id 
//      WHERE products.name = 'Product A';
9. 使用 Raw SQL 实现复杂查询 (Using Raw SQL for Complex Queries)
var orders []Order
db.Raw("SELECT * FROM orders WHERE user_id = ? AND amount > ? ORDER BY created_at DESC", userID, minAmount).Scan(&orders)// SQL: SELECT * FROM orders 
//      WHERE user_id = userID 
//      AND amount > minAmount 
//      ORDER BY created_at DESC;
10. 分页查询 (Pagination Queries)
var orders []Order
db.Where("user_id = ?", userID).Limit(10).Offset(20).Order("created_at DESC").Find(&orders)// SQL: SELECT * FROM orders 
//      WHERE user_id = userID 
//      ORDER BY created_at DESC 
//      LIMIT 10 OFFSET 20;

gorm中有很多相似的api 但是大致都可以实现想要的sql效果下面总结一下

Find
  • 功能:用于查询表中的所有记录或符合条件的记录,并将结果扫描到切片中。

  • 用法

    var students []Student
    db.Find(&students)
    

    你也可以结合条件使用 Find

    db.Where("age > ?", 18).Find(&students)
    
  • 适用场景:用于检索表中的多条记录,结果会自动填充到结构体切片中。

2. First
  • 功能:查询表中的第一条记录(按主键排序)。

  • 用法

    var student Student
    db.First(&student)
    

    你也可以结合条件使用 First

    
    db.Where("id = ?", 1).First(&student)
    
  • 适用场景:用于检索表中的单条记录(通常是按主键或条件查询到的第一条记录)。

3. Last
  • 功能:查询表中的最后一条记录(按主键排序)。

  • 用法

    var student Student
    db.Last(&student)
    

    你也可以结合条件使用 Last

    
    db.Where("age > ?", 18).Last(&student)
    
  • 适用场景:用于检索表中的最后一条记录(通常是按主键或条件查询到的最后一条记录)。

4. Take
  • 功能:查询表中的任意一条记录(不一定是第一条)。

  • 用法

    var student Student
    db.Take(&student)
    

    你也可以结合条件使用 Take

    
    db.Where("age > ?", 18).Take(&student)
    
  • 适用场景:用于检索表中的任意一条记录,常用于测试或简化查询。

5. RawScan
  • 功能

    • Raw:执行原始 SQL 查询。
    • Scan:将原始 SQL 查询的结果扫描到 Go 变量中。
  • 用法

    var students []Student
    db.Raw("SELECT * FROM students WHERE age > ?", 18).Scan(&students)
    
  • 适用场景:用于执行复杂的原始 SQL 查询,特别是当 GORM 的链式调用不够灵活时。Scan 用于将查询结果填充到自定义结构体或切片中。

6. Model
  • 功能:用于指定模型类型,通常在查询链中用于指定操作的表。

  • 用法

    var students []Student
    db.Model(&Student{}).Where("age > ?", 18).Find(&students)
    
  • 适用场景:在需要在查询中指定模型时使用,比如在链式调用中切换模型。

7. Table
  • 功能:用于指定操作的表名,通常用于原始 SQL 查询。

  • 用法

    var students []Student
    db.Table("students").Where("age > ?", 18).Find(&students)
    
  • 适用场景:用于在查询中指定表名,特别是当表名不是结构体名称或当执行原始 SQL 查询时。

感觉和mybatis-plus差不多其中的链式查询

最后写一个动态sql执行

func GetStudents(name string, age int, gender string) ([]Student, error) {var students []Studentdb := db.Model(&Student{})if name != "" {db = db.Where("name = ?", name)}if age > 0 {db = db.Where("age = ?", age)}if gender != "" {db = db.Where("gender = ?", gender)}err := db.Find(&students).Errorreturn students, err
}

模型定义

和mybatis-plus的思路一样

一对多

可以采用聚合集合的方式

type User struct {ID       uint      `gorm:"size:4"`Name     string    `gorm:"size:8"`Articles []Article // 用户拥有的文章列表
}type Article struct {ID     uint   `gorm:"size:4"`Title  string `gorm:"size:16"`UserID uint   // 属于   这里的类型要和引用的外键类型一致,包括大小User   User   // 属于
}
关联外键
type User struct {ID       uint      `gorm:"size:4"`Name     string    `gorm:"size:8"`Articles []Article `gorm:"foreignKey:UID"` // 用户拥有的文章列表
}type Article struct {ID    uint   `gorm:"size:4"`Title string `gorm:"size:16"`UID   uint   // 属于User  User   `gorm:"foreignKey:UID"` // 属于
}

事务

java中可以通过@Trantial注解完成这个效果 gorm中可以通过手动和自动方式实现

手动实现

func CreateUserWithOrders(db *gorm.DB) error {return db.Transaction(func(tx *gorm.DB) error {// 创建用户user := User{Name: "John"}if err := tx.Create(&user).Error; err != nil {return err // 回滚事务}// 创建订单order := Order{UserID: user.ID, Amount: 100}if err := tx.Create(&order).Error; err != nil {return err // 回滚事务}// 如果一切正常,事务会自动提交return nil})
}

多表操作时候才transaction中处理 如果参数异常就返回 数据库进行回滚 也可以db.Begin() api方式

func CreateUserWithOrders(db *gorm.DB) error {tx := db.Begin()// 创建用户user := User{Name: "John"}if err := tx.Create(&user).Error; err != nil {tx.Rollback() // 回滚事务return err}// 创建订单order := Order{UserID: user.ID, Amount: 100}if err := tx.Create(&order).Error; err != nil {tx.Rollback() // 回滚事务return err}return tx.Commit().Error // 提交事务
}

这篇关于快速学习gorm 框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096628

相关文章

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

gorm乐观锁使用小结

《gorm乐观锁使用小结》本文主要介绍了gorm乐观锁使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录前言grom乐观锁机制gorm乐观锁依赖安装gorm乐观锁使用创建一个user表插入数据版本号更新总结前言乐观锁,顾名

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R