快速学习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 driver
		DSN: "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作为主键
	//设置字段长度为2
	Name  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 uint
    Count  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 []Student
	db := 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).Error
	return 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 // 提交事务
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子不是胖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值