首页 > 编程学习 > Gorm 操作mysql

Gorm 操作mysql

发布时间:2022/9/22 0:34:50

gorm操作mysql

安装:

go get -u gorm.io/gorm   // gorm

要连接数据库首先要导入驱动程序:

import _ "github.com/go-sql-driver/mysql"

为了方便,grom包装一些驱动:

"gorm.io/driver/mysql"       // mysql,可以不使用上面那个了
"gorm.io/driver/postgres"    // postgres
"gorm.io/driver/sqlite"      // sqlite

go get -u gorm.io/driver/mysql

简单示例:

package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type UserInfo struct {
	ID      int       // 默认使用作为主键
	Name    string
	Gender  int
	Address string
}

func main() {
	dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
	d := mysql.Open(dsn)
	db, err := gorm.Open(d)
	if err != nil {
		panic(err)
	}

	// 自动迁移
	db.AutoMigrate(&UserInfo{})

	// 插入数据
	db.Create(&UserInfo{
		1, "zs", 0, "beijing",
	})

	var u UserInfo
	// 查询第一条数据
	db.First(&u)
	fmt.Printf("u: %v\n", u)

	// 更新数据
	db.Model(&u).Update("address", "上海")

	// 删除
	db.Delete(&u)
}

主键、表名、列名约定

默认情况下,GORM 使用 ID 作为主键。

type User struct{
    ID string    // 默认ID作为主键
    Name string
}

type Animal struct{
    AnimalID  int64 `gorm:"PRIMARY_KEY"`   // 使用AnimalID作为主键
    Name      string
}

使用结构体名的 蛇形复数 作为表名,例如:对于结构体 User,根据约定,其表名为 users


type Animal struct {
	ID       int64
	AnimalID int64 `gorm:"PRIMARY_KEY"`
	Name     string
}

func (Animal) TableName() string { // 修改表名为new_table_name_animal
	return "new_table_name_animal"
}

func (u User) TabelName() string { // 设置表名
	if u.Role == "admin" {
		return "admin_users"
	} else {
		return "users"
	}
}

字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAtDeleted_At 字段追踪创建、更新时间。

type User struct{
    ID            int64
    Name          string 
    Age           int64    `gorm:"column:age123"`  // 指定字段名为:age123
    MemberNumber  string                           // 创建的字段名为:member_number
}

grom.Model结构体

可以将它嵌入到您的结构体中,以包含这几个字段。

// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}


// 示例2:继承gorm.Model的例子
package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct{              // 不使用gorm.Model定义模型
    ID int
    Name string 
}

type Product struct {          // 使用gorm.Model定义模型,嵌入到我们结构体中。包含ID, Created_At, Updated_At, Deleted_At。
	gorm.Model
	Code   string
	Price  uint
	Status int8
}

func main() {
	// 想要正确的处理 time.Time ,您需要带上 parseTime 参数
	dsn := "root:123@tcp(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=True&loc=Local"
	d := mysql.Open(dsn)
	db, err := gorm.Open(d, &gorm.Config{})
	if err != nil {
		fmt.Printf("%v\n", err.Error())
		return
	}

	// 迁移schema,执行建表之类操作
	db.AutoMigrate(&Product{})

	// 1. insert新记录
	db.Create(&Product{
		Code:   "200",
		Price:  101,
		Status: 0,
	})
	// 2. 查询记录
	var product Product
	db.First(&product)    // 默认查找条件,查找id第一个。
	db.First(&product, 1) // id为1记录存在时,同上,查找id为1的记录。
	fmt.Printf("product: %+v\n", product)

	// 根据条件查询记录
	db.Find(&product, "code=?", "100") // 如果有多条记录,查询第一条记录
	fmt.Printf("product: %+v\n", product)

	// 3. 更新记录
	db.Model(&product).Update("status", 1)
	fmt.Printf("product: %+v\n", product)

	db.Model(&product).Updates(Product{Price: 200, Code: "code200"})                    // 更新多个字段
	db.Model(&product).Updates(map[string]interface{}{"Price": 300, "Code": "Code300"}) // 同上

	// // 4. 删除操作
	db.Delete(&product, 2) // 删除id=2的记录,只是更新deleted_at字段(逻辑删除)
	fmt.Printf("product: %+v\n", product)
}

结构体字段标签

tag名对大小写不敏感,推荐使用驼峰命名

结构体标记(tag) 说明
column 指定列名
type 指定列数据类型
size 指定列长度,默认255
primaryKey 设置为主键
UNIQUE 指定列唯一
DEFAULT 指定列默认值
PRECISION 指定列精度
NOT NULL 列非空
autoIncrement 自增
INDEX 创建索引
UNIQUE_INDEX 创建唯一索引
EMBEDDED 将结构体设置为嵌入
EMBEDDED_PREFIX 设置嵌入结构的前缀
- 忽略这个字段,不会在数据库中创建
foreignKey 指定外键,以【自己表】的哪个key去关联
references 指定引用表的列名,被关联结构的那个Key

示例:

package main

import (
	"database/sql"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name         string
	Age          sql.NullInt64 // 可以为null的int64类型, 因为sql的null不能转为int
	Birthday     *time.Time
	Email        string  `gorm:"type:varchar(100);unique_index"` // 类型varchar, 唯一索引
    Role         string  `gorm:"size:255;comment:'角色'"`        // 设置字段长度为255, 添加注释
	MemberNumber *string `gorm:"unique;not null"`                // 设置字段唯一,不能为空
	Num          int     `gorm:"AUTO_INCREMENT"`                 // 设置字段自增
	Address      string  `gorm:"index:addr"`                     // 添加字段索引,索引名addr
	IgnoreMe     int     `gorm:"-"`                              // 忽略此字段,不会创建在数据库中
}

type Animal struct {
	ID       int64
	AnimalID int64 `gorm:"PRIMARY_KEY"`
	Name     string
}

func main() {
	dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
	d := mysql.Open(dsn)
	db, err := gorm.Open(d, &gorm.Config{})
	if err != nil {
		panic(err)
	}

	db.AutoMigrate(&User{})
	db.AutoMigrate(&Animal{})
}

插入操作

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 1. 定义模型
type User struct {
	ID   int64
	Name string
	Age  int64
}

func main() {
	dsn := "root:123@(localhost:3306)/learn_gorm?charset=utf8mb4&parseTime=true"
	d := mysql.Open(dsn)
	db, err := gorm.Open(d, &gorm.Config{})
	if err != nil {
		panic(err)
	}

	// 2. 模型与数据库中表对应
	db.AutoMigrate(&User{})

	// 3. 创建记录
	var u = User{Name: "zs", Age: 19}
	db.Create(&u) // 传&u。 否则报panic
}


// 示例2. 设置默认值
type User struct {
	ID   int64
	Name string `gorm:"default:李四"`
	Age  int64
}

func main(){
	var u2 = User{Age: 21}
	db.Create(&u2)
}

// 示例3: 设置default的tag之后,零值不会插入到数据库。
type User struct {
	ID   int64
	Name string `gorm:"default:李四"`
	Age  int64
}

var u2 = User{Age: 21, Name: ""}    // 同User{Age: 21},数据库会插入【李四】,而不是被空字符覆盖。
db.Debug().Create(&u2)              // 在语句之前调用Debug()打印sql语句。


// 示例4:使用指定字段插入数据
type User struct {
	ID      int64
	Name    *string `gorm:"default:李四"`
	Age     *int64  `gorm:"default:18"`
	Address string
}

name := "zs"
var age int64 = 100
u := User{Name: &name, Age: &age, Address: "bj"}
db.Debug().Select("Name", "Address").Create(&u)   //  INSERT INTO `users` (`name`,`address`) VALUES ('zs','bj')  
/**  数据库中:
3 | zs   |   18 | bj      |
**/ 


// 示例5: 批量插入
type User3 struct {
	ID   int64
	Name string
	Age  int64
}
var users = []User3{User3{Name: "zs"}, User3{Name: "ls"}, User3{Name: "ww"}}
db.Debug().Create(&users)            // 一条sql插入


var users = []User3{User3{Name: "zs_new"}, User3{Name: "ls_new"}, User3{Name: "ww_new"}, User3{Name: "zl"}, User3{Name: "heihei"}}
db.Debug().CreateInBatches(&users, 2) // 多条sql,一条sql插入2条记录


// 示例6: 使用map插入
user := map[string]interface{}{
    "Name": "gaga", "Age": 19,
}
db.Model(&User3{}).Create(&user)

// map批量插入
users := []map[string]interface{}{
		{"Name": "gaga_new1", "Age": 29},
		{"Name": "gaga_new2", "Age": 18},
	}
db.Model(&User3{}).Create(&users)

注意:

  • 对于声明了默认值的字段,在插入新记录的时候,会忽略零值(0,"",false, nil等)的字段。需要使用指针类型实现Scanner/Valuer接口 来避免这个问题。

    // 方法一: 声明指针类
    type User struct{
        ID int64
        Name *string `gorm:"default:李四"`   // 为*string类型
        Age *int64 `gorm:"default:18"`
    }
    
    func main(){
        var u User = User{Name:new(string), Age:new(int64)}  // 插入零值
        db.Create(&u)    
    }
    /**
        | id | name   | age  |
        |  1 |        |    0 |
    **/
    
    // 方法二:使用实现了Scanner/Valuer接口的结构体
    type User2 struct {
    	ID   int64
    	Name sql.NullString `gorm:"default:李四"`
    	Age  sql.NullInt64  `gorm:"default:18"`
    }
    
    func main(){
        var u2 = User2{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{0, true}}
    	db.Create(&u2)
    }
    
    // 如果不想再数据库中设置默认值
    type User2 struct {
    	ID   int64
    	Name sql.NullString `gorm:"default:李四"`
    	Age  sql.NullInt64  `gorm:"default:18"`
    }
    
  • 如果在迁移时候,跳过默认值定义,可以在tag中添加default:(-)

查询操作

一般查询

var user User
// 1. 查询id排序的第一条记录
db.First(&user) // select * from users order by id limit 1;
fmt.Printf("user: %+v\n", user)

// 2. 查询第一条记录
db.Take(&user) // select * from users limit 1;
fmt.Printf("user: %+v\n", user)

// 3. 查询id逆序第一条记录
db.Last(&user)
fmt.Printf("user: %+v\n", user) // select * from users order by id desc limit 1;

result := db.First(&user)
fmt.Printf("result.RowsAffected: %v\n", result.RowsAffected) // 找到的记录数
fmt.Printf("result.Error: %v\n", result.Error)               // return errors or nil
}

// 4. 查询所有记录
var users = make([]User, 0, 10)
db.Find(&users)   // select * from users;
fmt.Printf("users: %v\n", users)

// 5. 查询制定id的记录
var u User
db.First(&u, 3)  // select * from users where id = 3; 
fmt.Printf("u: %v\n", u)

条件查询

// 1. 查询指定条件的第一条记录
var user User
db.Where("name=?", "zs").First(&user) // select * from users where name="zs" order by id limit 1;
fmt.Printf("user: %+v\n", user)

// 2. 查询制定条件的所有记录
var users = make([]User, 0, 10)
db.Where("name=?", "zs").Find(&users) // select * from users where name="zs"
fmt.Printf("users: %+v\n", users)

// 3. <> 不等于
users = make([]User, 0, 10)
db.Where("name<>?", "zs").Find(&users) // select * from users where name<>'zs'
fmt.Printf("users: %+v\n", users)

// 4. in
users = make([]User, 0, 10)
db.Where("name in (?)", []string{"zs", "ls"}).Find(&users) // select * from users where name in ("zs", "ls");
fmt.Printf("users: %+v\n", users)

// 5. like语句
users = make([]User, 0, 10)
db.Where("name like ?", "%zs%").Find(&users)  // select * from users where name like %zs%;
fmt.Printf("users: %+v\n", users)

// 6. and语句
users = make([]User, 0, 10)
db.Where("name=? and age=?", "zs", 18).Find(&users) // select * from users where name=zs and age = 18
fmt.Printf("users: %+v\n", users)

// 7. >语句
lastWeek := time.Date(2022, 9, 13, 1, 1, 1, 1, time.Local).Format("2006-01-02 15:04:05")
fmt.Printf("lastWeek: %v\n", lastWeek)  // 2022-09-13 01:01:01
users = make([]User, 0, 10)
db.Where("updated_at > ?", lastWeek).Find(&users)  // select * from users where updated_at > "2022-09-13 01:01:01"
fmt.Printf("users: %+v\n", users)

// 8. between 语句
users = make([]User, 0, 10)
db.Where("age between ? and ?", 10, 20).Find(&users)  // select * from users where age between 10 and 20;
fmt.Printf("users: %+v\n", users)

struct和map查询

// 1. 结构体作为查询条件
var user User
db.Where(&User{Name: "zs", Age: 18}).First(&user) // select * from users where name = zs and age = 18
fmt.Printf("user: %+v\n", user)

// 2. map作为查询条件
var users = make([]User, 0, 10)
db.Where(map[string]interface{}{"Name": "zs", "Age": 18}).Find(&users) // select * from users where name = zs and age = 18
fmt.Printf("users: %v\n", users)

// 3. 主键id作为查询条件
users = make([]User, 0)
db.Where([]int64{1, 3, 5}).Find(&users) // select * from users where id in (1, 3, 5)
fmt.Printf("users: %v\n", users)

注意:

  • 在使用结构体查询的时候,GORM只会通过非零字段,一些字段的零值不会用于到查询条件中。 map不受影响。

    var user User
    db.Debug().Where(&User{Name: "zs", Age: 0}).First(&user) // select * from users where name = zs, 零值的Age不会用于查询条件
    fmt.Printf("user: %+v\n", user)
    
    // 解决办法:    使用指针 或者  实现Scanner/Valuer接口。
    
    // 法一:使用指针
    type User struct{
        Name string
        Age *int64
    }
    var age int64 = 0
    db.Where(&User{Name: "zs", Age: &age}).First(&user)  //  select * from users where name = zs and age = 0
    
    // 法二:使用scanner和Valuer接口的类型
    type User struct{
        Age sql.NullInt64
    }
    db.Where(&User{Name: "zs", Age: sql.NullInt64{0, true}}).First(&user) // select * from users where name = zs and age = 0
    

Not条件

// 1. 基本使用
var users = make([]User, 0)
db.Not("name=?", "zs").Find(&users) // select * from users where not name = "zs"
fmt.Printf("users: %v\n", users)

// 2. struct。 在not中使用and
db.Not(User{Name: "zs", Age: 18}).Find(&users) // select * from users where name <> zs and age <> 18;
fmt.Printf("users: %v\n", users)

// 3. map。在not中使用and
db.Not(map[string]interface{}{"Name": "zs", "Age": 18}).Find(&users)  // select * from users where name <> zs and age <> 18;
fmt.Printf("users: %v\n", users)

Or条件

// 1. 基本使用
var users = make([]User, 0)
db.Where("name=?", "zs").Or("age=?", 18).Find(&users)  // SELECT * FROM `users` WHERE (name='zs' OR age=18)
fmt.Printf("users: %v\n", users)

// 2. struct。 在or中使用and
db.Where("name=?", "zs").Or(User{Name: "zs-new", Age: 18}).Find(&users) // SELECT * FROM users WHERE (name='zs' OR (name = 'zs-new' AND age = 18))
fmt.Printf("users: %v\n", users)

// 3. map
users = make([]User, 0)
db.Where("name=?", "zs").Or(map[string]interface{}{"Name": "zs-new", "Age": 18}).Find(&users) // SELECT * FROM users WHERE (name='zs' OR (name = 'zs-new' AND age = 18))
fmt.Printf("users: %v\n", users)

指定字段

// 1. 只查询指定的字段
users = make([]User, 0)
db.Select("name", "age").Find(&users) // select name,age from users。   
fmt.Printf("users: %+v\n", users)             // 注意: 此时name和age赋值给users的值,其余字段为零值。

// 2. 只查询指定的字段, 同上
db.Debug().Select([]string{"name", "age"}).Find(&users) // select name,age from users
fmt.Printf("users: %v\n", users)

Order

// 1. 基本使用
users = make([]User, 0)
db.Order("name desc, age").Find(&users) // select * from users order by name desc, age;
fmt.Printf("users: %v\n", users)

// 2. 同上
db.Order("name desc").Order("age").Find(&users) // select * from users order by name desc, age;
fmt.Printf("users: %v\n", users)

limit 和 offset

users = make([]User, 0)
db.Debug().Limit(1).Offset(2).Where("name=?", "zs").Find(&users)  // select * from users where name = "zs" limit 1 offset 2;
fmt.Printf("users: %v\n", users)

// 示例2
var users = make([]User, 0)
db.Limit(5).Offset(-1).Find(&users)  // Offset设置为-1或0,取消offset。  SELECT * FROM `users` LIMIT 5
fmt.Printf("users: %+v\n", users)

注意:使用count不能用Offset,或者是将Offset值设为 -1

group和having

// 1. 定义Result的结构体
type Result struct {
	Name     string
	TotalSum int64
}

func main(){
	var result = make([]Result, 0, 10)
    
    // 注意:1.需要指定表,db.Model() ; 2. Select中的字段名和结构体Result要对应。
	db.Debug().Model(&User{}).Select("name, sum(age) as total_sum").Group("name").Find(&result) 
	fmt.Printf("result: %v\n", result)
}

Distinct

// 1. 返回结果赋值给User结构体
var users = make([]User, 0, 10)
db.Debug().Distinct("name", "age").Find(&users)
fmt.Printf("users: %v\n", users)

// 2. 返回结果赋值给新的结构体
type Result struct {
	Name string
	Age  int64
}

func main(){
    var results = make([]Result, 0, 10)
	db.Debug().Model(&User{}).Distinct("name", "age").Find(&results)  // 需要调用db.Model指定表名  *** 
	fmt.Printf("users: %v\n", users)
}

Count

会执行sql查询操作。类似Find()

var count int64

db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count) // SELECT count(1) FROM users WHERE name = 'jinzhu';

db.Table("deleted_users").Count(&count) // SELECT count(1) FROM deleted_users;

db.Model(&User{}).Distinct("name").Count(&count) // SELECT COUNT(DISTINCT(`name`)) FROM `users`

db.Table("deleted_users").Select("count(distinct(name))").Count(&count)  // SELECT count(distinct(name)) FROM deleted_users

// 注意:
db.Debug().Find(&users).Count(&count)  // 会产生两条sql, 一条Find,一条Count
db.Model(&User{}).Count(&count)        // 产生一条sql,查询count

db.Debug().Model(&User{}).Limit(2).Count(&count)  // SELECT count(*) FROM `users` LIMIT 2。  查询出来的结果为count(*),再limit 2。
db.Debug().Model(&User{}).Limit(2).Offset(2).Count(&count)  // SELECT count(*) FROM `users` LIMIT 2 OFFSET 2。查询出来的结果为count(*),就一行,在offset 2,就没有结果了。

更新操作

// 1. 更新所有字段
type User struct{
    gorm.Model
    Name string 
    Age int64
    Active bool
}

func main(){
    u := User{}
    db.First(&u)
  	
    u.Name = "张三"
    u.Age = 100
    db.Save(&u)  // 默认会将user的所有字段都进行update操作,如果新对象(没有插入)则进行insert操作。  UPDATE `users` SET `created_at`='2022-09-15 11:50:38.67',`updated_at`='2022-09-15 20:34:10.821',`deleted_at`=NULL,`name`='张三',`age`=50,`active`=true ;
}


// 2. 更新指定字段Update
db.Model(&u1).Update("name", "world")                            // 更新u1记录的name为world
db.Model(&u1).Where("active=?", true).Update("name", "world")    // 更新u1记录【带条件】的name为world


// 3. 更新多个字段Updates。 使用struct和map
db.Model(&user).Updates(User{Name: "王五", Age: 18, Active: false})                         // 注意当使用struct,【存在零值不会更新的问题】
db.Model(&user).Updates(map[string]interface{}{"name": "王五", "age": 18, "active": false}) // 使用map更新多个字段


// 4. 更新选定的字段。
db.Model(&user).Select("name", "age").Updates(User{Name: "王五1", Age: 118, Active: true}) // 传进来的是struct, 但只更新name和age
db.Model(&user).Select("name", "age").Updates(map[string]interface{}{"name": "王五2", "age": 128, "active": false}) // 传进来的是map, 但只更新name和age
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "王五3", "age": 10, "active": true}) // 【忽略name字段】。更新除了name字段之外的字段

// 5. 批量更新记录
db.Model(&User{}).Where("active=?", false).Updates(User{Name: "hello", Age: 11}) // 批量更新记录
db.Table("users").Where("active=?", false).Updates(User{Name: "world", Age: 18}) // 对表users批量更新记录

删除操作

// 1. 删除记录
u := User{}
u.ID = 1
db.Delete(&u) // 删除id为1的记录

u = User{}
u.Name = "zs"
db.Debug().Where("name=?", "zs").Delete(&u) // 批量删除

// 2. 根据主键删除
db.Delete(&User{}, 3)              // 删除id=3记录
db.Delete(&User{}, "3")            // 同上
db.Delete(&User{}, []int{1, 2, 3}) // 删除多个id的记录

// 3. 批量删除
db.Where("name = ?", "zs").Delete(&User{})
db.Delete(&User{}, "name=?", "ls")

// 4. 物理删除
db.Debug().Unscoped().Where("name is not null").Delete(&User{}) // 物理删除

注意:

  • 删除记录时候,【确保主键字段(有值)】,gorm会通过主键删除记录,如果主键字段为空,会删除所有记录。

关联关系

一对一关系

// 第一种:belongs to 
// Dog 属于 GirlGod
type Dog struct {
	gorm.Model
	Name string

	GirlGodId uint        // 在Dog表中添加GirlGodId外键
	GirlGod   GirlGod     // 添加GirlGod
}

type GirlGod struct {
	gorm.Model
	Name string
}

func main() {
	DB.AutoMigrate(&Dog{}, &GirlGod{})
	
    // 1. 添加记录
	girlGod := GirlGod{
		Name: "女神1",
	}

	d := Dog{
		Name:    "狗1",
		GirlGod: girlGod,
	}
	DB.Create(&d) // 对dog添加记录的同时,会自动创建GirlGod记录。
    
    // 2. 查询
    var dog Dog
	DB.First(&dog, 1) // 此时在返回结果中,没有GirlGod记录
	fmt.Printf("dog: %v\n", dog)

	var dog2 Dog
	DB.Preload("GirlGod").First(&dog2, 1) // 通过Preload()函数在加载Dog同时,加载GirlGod记录
	fmt.Printf("dog2: %v\n", dog2)
}


// 第二种:Has one 
type Dog struct {
	gorm.Model
	Name string

	GirlGodId uint // 在Dog表中添加GirlGodId外键
}

type GirlGod struct {
	gorm.Model
	Name string

	Dog Dog // 添加Dog变量
}

func main() {
	DB.AutoMigrate(&GirlGod{}, &Dog{})
	
    // 1. 添加记录
	girlGod := GirlGod{
		Name: "女神1",
	}

	d := Dog{
		Name: "狗1",
	}
	girlGod.Dog = d                   // 不添加这句话,不会自动创建Dog记录,赋值之后,Create才自动创建Dog记录
	DB.Create(&girlGod) 
    
    
    // 2. 查询
    var girl GirlGod
	DB.First(&girl, 2)                 // 此时在返回结果中,没有Dog记录
	fmt.Printf("girl: %v\n", girl)

	var girl2 GirlGod
	DB.Preload("Dog").First(&girl2, 2) // 通过Preload()函数在加载GirlGod同时,加载Dog记录
	fmt.Printf("girl2: %v\n", girl2)
}


一对多关系

type Dog struct {
	gorm.Model
	Name string

	GirlGodId uint // 在Dog表中添加GirlGodId外键
}
type GirlGod struct {
	gorm.Model
	Name string

	Dogs []Dog     // 同一对多不同是,这里是切片
}

func main() {
	DB.AutoMigrate(&GirlGod{}, &Dog{})

	// 1. 添加记录
	d1 := Dog{
		Name: "dog1号",
	}
	d2 := Dog{
		Name: "dog2号",
	}
	g := GirlGod{
		Name: "女神1",
		Dogs: []Dog{d1, d2},   
	}

	DB.Create(&g)   // 添加girlGod同时,添加两条dog记录

	// 2. 一对多的查询
	var girl GirlGod
	DB.Debug().Preload("Dogs").First(&girl) // 将Dog两条记录都打印出来了
	fmt.Printf("girl: %v\n", girl)
    
    // 3. 带条件的预加载preload
	var girl2 GirlGod
	DB.Preload("Dogs", "name=?", "dog2号").First(&girl2)
	fmt.Printf("girl2: %v\n", girl2)
    
    // 4. 自定义预加载
	var girl3 GirlGod
	DB.Preload("Dogs", func(db *gorm.DB) *gorm.DB {   // 定义匿名函数
		return db.Where("name=?", "dog2号")
	}).Find(&girl3)
	fmt.Printf("girl3: %+v\n", girl3)
    
    // 5. 嵌套加载
	var girl4 GirlGod
	DB.Preload("Dogs.Info").Preload("Dogs").First(&girl4) // 查询girls,带Dogs,带Info
	fmt.Printf("girl4: %v\n", girl4)
}


// 对外键和引用重写
// 默认情况下,第二个表的外键,引用第一个表的主键。   例如:Dog表的外键GirlGodID引用的是GirlGod的主键ID。
// 可以对外键和引用重写。
package main

type User struct {
	ID   uint
	Name string `gorm:"primaryKey;type:varchar(36);"`

	// Cards []Card `gorm:"foreignKey:Number;"`             // 此时Number作为外键,引用User的主键ID
	Cards []Card `gorm:"foreignKey:Number;references:Name"` // 此时Number作为外键,引用User的Name,此时需要设置字段类型和主键
}

type Card struct {
	ID     uint
	Number string `gorm:"type:varchar(36)"`

	UserID uint
}

func main() {
	DB.AutoMigrate(&User{}, &Card{})
	c1 := Card{Number: "1000abc"}
	c2 := Card{Number: "2000abc"}

	user := User{Name: "zs", Cards: []Card{c1, c2}}
	DB.Create(&user)
}

多对多关系

type Info struct {
	gorm.Model
	Money uint

	DogId uint // dog的外键, 一对一
}

// 一个Dog 有多个女神GirlGod
type Dog struct {
	gorm.Model
	Name string

	Info Info

	GirlGods []GirlGod `gorm:"many2many:dog_girl_god"`      // 添加GirlGod切片, 需要添加tag,其中dog_girl_god是连接表
}

// 一个女神有多个Dog
type GirlGod struct {
	gorm.Model
	Name string

	Dogs []Dog `gorm:"many2many:dog_girl_god"`             // 添加many2many 的tag,否则报错,其中dog_girl_god是连接表
}

func Many2Many() {
	DB.AutoMigrate(&Dog{}, &GirlGod{}, &Info{})
	
    // 1. 添加记录
	i := Info{
		Money: 3000,
	}
	g1 := GirlGod{
		Name: "女神1号",
	}
	g2 := GirlGod{
		Name: "女神2号",
	}
	d := Dog{
		Name:     "狗子1号",
		Info:     i,
		GirlGods: []GirlGod{g1, g2},
	}
	DB.Create(&d) // 添加记录
    
    // 2. 查询
    var dogs []Dog
	DB.Model(&Dog{}).Preload("GirlGods").Find(&dogs) // 查询所有Dogs记录
	fmt.Printf("dogs: %+v\n", dogs)

	var girlGods []GirlGod
	DB.Model(&GirlGod{}).Preload("Dogs").Find(&girlGods) // 查询所有GirlGods记录
	fmt.Printf("girlGods: %+v\n", girlGods)
}

事务操作

// 1. 禁用事务
dsn := "root:123@tcp(127.0.0.1:3306)/gorm_new?charset=utf8mb4&parseTime=true&loc=Local"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
    SkipDefaultTransaction: true,
})


// 2. Transaction() 返回nil提交事务,返回error,则rollback
DB.Transaction(func(tx *gorm.DB) error {
    tx.Create(&TMG{Name: "NAME1"})
    tx.Create(&TMG{Name: "NAME2"})
    tx.Create(&TMG{Name: "NAME3"})
    return nil
})

// 3. 手动事务
tx := DB.Begin()

tx.Create(&TMG{Name: "NAME1111"})
tx.Create(&TMG{Name: "NAME2222"})
tx.Create(&TMG{Name: "NAME3333"})

//tx.Rollback()
tx.Commit()
Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号