快速学习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

相关文章

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学