go整合gorm

快速开始

ORM全称是:0bject Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。举例来说就是,我定义一个对象,那就对应着一张表,这个对象的实例,就对应着表中的一条记录。
对于数据来说,最重要最常用的是表:表中有列,orm就是将一张表映射成一个类,表中的列映射成类中的一个类。java、python,但是针对go语言而言,struct,就是列如何映射,是因为列可以映射成struct中的类型int->int,但是有另一个问题? 就是数据库中的列具备很好的描述性!
执行sql,需要我们有足够的sql语句基础、需要我们懂得不同的数据的sql

安装依赖

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

引入gorm

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

// 创建一个结构体和数据库表做映射
type Product struct {
	gorm.Model
	Code  string
	Price uint
}

func main() {
	coon, err := getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	coon.AutoMigrate(Product{})
}

// 获取链接对象
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             time.Second, // Slow SQL threshold
			LogLevel:                  logger.Info, // Log level
			IgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for logger
			ParameterizedQueries:      true,        // Don't include params in the SQL log
			Colorful:                  true,        // Disable color
		},
	)

	var dbConnect *gorm.DB
	var gromErr error
	url := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", user, password, host, port, dbName)
	dbConnect, gromErr = gorm.Open(mysql.Open(url), &gorm.Config{
		Logger: newLogger,
	})
	if err != nil {
		// panic("failed to connect database")
		return nil, gromErr
	} else {
		log.Println("连接成功")
		return dbConnect, gromErr
	}
}


执行以上代码会在test数据库创建一个数据表

名为product

操作表

根据结构体建表

在下边的示例中

定义一个user结构体

gormdb.Migrator().CreateTable() 方法中,可以传入结构体可变参数

同通常情况下,数据库自动创建的表的名字是结构体的复数形式

执行下边的代码会在数据库中创建一个users的表

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

// 定义一个user结构体
type User struct {
	ID   int    `gorm:"primaryKey"`       // 主键
	Name string `gorm:"type:varchar(20)"` // 姓名
	Sex  string `gorm:"type:varchar(20)"` // 性别
	Age  int    `gorm:"type:int"`         // 年龄
}


var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}

    // 根据user结构体创建表
	gormdb.Migrator().CreateTable(&User{})
}

func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// 获取数据库连接
}

指定表名

可以通过table方法在自动创建表时候指定表名

gormdb.Table("users2").Migrator().CreateTable(&User{})

执行上边的代码

会参照User结构体的参数,创建一个名为user2的表名

也可以在给结构体实现TableName()方法

type User struct {
	ID   int    `gorm:"primaryKey"`       // 主键
	Name string `gorm:"type:varchar(20)"` // 姓名
	Sex  string `gorm:"type:varchar(20)"` // 性别
	Age  int    `gorm:"type:int"`         // 年龄
}

// 实现TableName方法 返回的字符串值就是数据库的表名
func (User) TableName() string {
	return "usertable"
}

给结构体实现TableName()方法

此时通过gormdb.Migrator().CreateTable(&User{})

方法执行建表语句的时候,会在数据库创建一个名为 usertable 的数据表

删除表

通过Migrator().DropTable() 传入一个结构体,可删除users表

gormdb.Migrator().DropTable(&User{})

判断表是否存在

flag := gormdb.Migrator().HasTable(&User{})
	fmt.Println(flag)

判断是否有Users表

返回一个bool值

快速体验crud

创建数据

创建一条数据

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(&Product{})
	createdData()
}

// 插入数据
func createdData() {
	prod := Product{Code: "001", Price: 123445}
	result := gormdb.Create(&prod)
	fmt.Println("返回插入数据的主键", prod.ID)
	fmt.Println("返回 error", result.Error)
	fmt.Println("返回插入记录的条数", result.RowsAffected)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

批量创建

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(&Product{})
	createdManyData()
}

// 批量插入数据
func createdManyData() {
	prods := []Product{{Code: "001", Price: 123445}, {Code: "002", Price: 54321}}
	result := gormdb.Create(&prods)
	for _, prod := range prods {
		fmt.Println("返回插入数据的主键", prod.ID)
	}

	fmt.Println("返回 error", result.Error)
	fmt.Println("返回插入记录的条数", result.RowsAffected)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

查询数据

First, Take, Last

GORM 提供 First, Take, Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件。当没有找到记录时,它会返回错误 ErrRecordNotFound

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(&Product{})
	selectData()
}

func selectData() {
	var firstProd Product
	gormdb.First(&firstProd) // 获取第一条记录(主键升序)
	fmt.Println("First", firstProd.ID)

	var takeProd Product
	gormdb.Take(&takeProd) // 获取一条记录,没有指定排序字段
	fmt.Println("Take", takeProd.ID)

	var lastProd Product
	gormdb.Last(&lastProd) // 获取最后一条记录(主键降序)
	fmt.Println("Last", lastProd.ID)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

获取返回对象

返回对象用来判断操作是否成功,影响的数据行数等

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(&Product{})
	selectDataResult()
}

// 获取返回对象
func selectDataResult() {
	var firstProd Product
	result := gormdb.First(&firstProd) // 获取第一条记录(主键升序)
	fmt.Println("检查 ErrRecordNotFound 错误", result.ErrRecordNotFound)
	fmt.Println("returns error", result.Error)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	newLogger := logger.New(
		log.New(os.Std// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

获取全部记录

gorm可以通过Find获取一个表里的全部记录

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(&Product{})
	selectAll()
}

// 获取全部记录
func selectAll() {
	var prods []Product
	result := gormdb.Find(&prods) // 获取全部记录(主键升序)

	for _, v := range prods {
		fmt.Println(v)
	}
	fmt.Println("返回找到的记录数", result.RowsAffected)
	fmt.Println("returns error", result.Error)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

条件查询

String 条件
// 获取第一条匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// 获取全部匹配的记录
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';
Struct条件
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
Map条件
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
主键切片条件
// 主键切片条件
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);
Not 条件

构建 NOT 条件,用法与 Where 类似

db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;

// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;

// 不在主键切片中的记录
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
链式调用多个 Where 条件

有时候前端传递过来的参数不确定,需要动态拼接wheresql语句

// 链式条件查询
func manyCondition() {
	var prods []Product
	db := gormdb
	tx := db.Where("id != ?", "''") // were会返回tx对象,可以根据条件动态向tx对象加入条件
	if true {
		tx.Where("code = ?", "001")
	}
	if true {
		tx.Where("price = ?", 123445)
	}
	tx.Find(&prods) // 获取第一条记录(主键升序)
	for _, v := range prods {
		fmt.Println(v)
	}
}
Or 条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
选择特定字段

选择您想从数据库中检索的字段,默认情况下会选择全部字段

db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;
Group & Having
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`


db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
分页查询

分页操作是为了减少前端对后端请求的压力,对于一个系统,为了提高访问效率,不需要每次从后端请求全量的数据,采用分页的方式,获取指定页码的数据,页数(page)和每页的大小(page_size)都可以单独指定

package main

import (
	"fmt"
	"log"
	"math"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	// gormdb.AutoMigrate(&Product{})
	manyCondition()
}

// 链式条件查询
func manyCondition() {
	page := 1
	pageSize := 2

	var prods []Product

	var totalCount int64
	db := gormdb
	tx := db.Where("id != ?", "''")
	if true {
		tx.Where("price = ?", 123445)
	}

	tx.Scopes(Paginate(page, pageSize)).Find(&prods)
	// 获取总页数
	tx.Model(&prods).Count(&totalCount)
	totalPages := int(math.Ceil(float64(totalCount) / float64(pageSize)))
	fmt.Println("总页数", totalPages)

	// 打印命中的数据
	for _, v := range prods {
		fmt.Println(v.ID)
	}
}

/*
*
分页封装
*/
func Paginate(page int, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page == 0 {
			page = 1
		}
		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}
		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

Scan

Scan 结果至 struct,用法与 Find 类似

Scan 方法用于将结果扫描到另一个结构体中,这个结构体可能与原始模型的结构不同。这在你只需要查询的结果的子集,或者结果需要映射到不同的结构体时非常有用。例如

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	// gormdb.AutoMigrate(&Product{})
	scan()
}

// scan查询
func scan() {
	var prods []Product
	gormdb.Table("products").Select("code", "price").Where("price = ?", "2").Scan(&prods)
	// 打印命中的数据
	for _, v := range prods {
		fmt.Println(v)
	}
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

高级查询

Find 至 map

GORM 允许扫描结果至 map[string]interface{}[]map[string]interface{},此时别忘了指定 ModelTable,例如:

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	// gormdb.AutoMigrate(&Product{})
	findMap()
}

// 将结果映射到map
func findMap() {
	var result map[string]interface{}
	gormdb.Table("products").Find(&result)
	// 打印命中的数据
	fmt.Println(result)
	for key, v := range result {
		fmt.Println(key, v)
	}

	var results []map[string]interface{}
	gormdb.Table("products").Find(&results)
	for _, v := range results {
		for key1, v1 := range v {
			fmt.Println(key1, v1)
		}
	}
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

更新数据

save保存所有字段

Save 会保存所有的字段,即使字段是零值

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Product struct {
	gorm.Model
	Code  string
	Price uint
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.153.146", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	// gormdb.AutoMigrate(&Product{})
	upDate()
}

// 更新字段
func upDate() {
	var product Product
	gormdb.Find(&product, 1)
	product.Code = "00211312"
	gormdb.Save(&product)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

删除

删除一条记录

// 删除一条已有的记录(email 的主键值为 10)
db.Delete(&email)
// DELETE from emails where id=10;

// 通过内联条件删除记录
db.Delete(&Email{}, 20)
// DELETE from emails where id=20;

// 带上其它条件
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE FROM emails WHERE id=10 AND name = 'jinzhu'

软删除

如果您的模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

拥有软删除能力的模型调用 Delete 时,记录不会被数据库。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过普通的查询方法找到该记录

db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

查找被软删除的记录

您可以使用 Unscoped 找到被软删除的记录

db.Unscoped().Where("age = 20").Find(&users)

永久删除

您也可以使用 Unscoped 永久删除匹配的记

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;

原生sql

gorm支持原生sql操作

原生 SQL 查询

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Person struct {
	ID   int    `gorm:"primaryKey"`       // 主键
	Name string `gorm:"type:varchar(20)"` // 姓名
	Sex  string `gorm:"type:varchar(20)"` // 性别
	Age  int    `gorm:"type:int"`         // 年龄
}

// 实现TableName方法 返回的字符串值就是数据库的表名
func (Person) TableName() string {
	return "persons"
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}

	gormdb.AutoMigrate(Person{})
	NativeSql()
}

func NativeSql() {
	var person []Person
	gormdb.Raw("select * from persons").Find(&person)
	fmt.Println(person)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

控制台打印

[{1 孙悟空 男 19} {2 猪八戒 男 22}]

原生SQL添加

func main() {
	NativeSql()
}

func NativeSql() {
	gormdb.Exec("insert into persons (name, sex, age) value (?,?,?)", "白龙马", "男", 43)
}

控制台输出

insert into persons (name, sex, age) value (?,?,?)

原生SQL删除

func main() {
	NativeSql()
}

func NativeSql() {
	gormdb.Exec("delete from persons where id = ?", 1)
}
delete from persons where id = ?

原生SQL修改

将persons表中id为2的数据的name改成天棚元帅

func main() {
	NativeSql()
}

func NativeSql() {
	gormdb.Exec("update persons set name = ? where id = ?", "天棚元帅", 2)
}

多表操作

一对一

1对1映射

学生和学生详情是一对一的关系

在下边的示例中创建2个结构体

Student结构体中包含一个StudentInfo的结构体

在StudentInfo字段后边加注解

ForeignKey表示外键 这里指向 StudentInfo.UserId字段

AssociationForeignKey表示关联外键 这里指向Student.ID字段

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

1对1查询

Association 方式
package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

func (StudentInfo) TableName() string {
	return "student_info"
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.124.186", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToOneSelect()
}

func OneToOneSelect() {
	var student Student = Student{}
	gormdb.First(&student)
	fmt.Println(student) // 这里student.StudentInfo是没有值的
	gormdb.Model(&student).Association("StudentInfo").Find(&student.StudentInfo)
	fmt.Println(student) // 这里可以正常打印出来student.StudentInfo的值

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

PreLoad方式
// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

func (StudentInfo) TableName() string {
	return "student_info"
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "123456", "192.168.124.186", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToOneSelectPreload()
}

func OneToOneSelectPreload() {
	var student Student = Student{}
    // 在这里Preload函数传入的参数为 
	gormdb.Preload("StudentInfo").First(&student, 1)
	fmt.Println(student)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

1对1添加

下面的示例是将学生包括学生关联的1对1数据直接插入数据库中

这里注意并没有指定学生主键和学生详细信息主键和学生的外键

这些数据由框架直接生成

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

func (StudentInfo) TableName() string {
	return "student_info"
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(Student{}, StudentInfo{})
	OneToOneCreate()
}

// 添加1对1关系数据
func OneToOneCreate() {
	s := Student{
		Name: "六小灵通",
		Age:  18,
		StudentInfo: StudentInfo{
			IdCard: "320106199311040612",
		},
	}
	gormdb.Create(&s)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

1对1更新

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

func (StudentInfo) TableName() string {
	return "student_info"
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToOneUpdate()
}

// 一对一关联更新
func OneToOneUpdate() {
	var student Student = Student{}
	// 在这里要先查询再更新
	gormdb.Preload("StudentInfo").Find(&student, "ID = ?", 2)

	gormdb.Model(student.StudentInfo).Update("id_card", "110")
	fmt.Println(student)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

1对1删除

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID          int    `gorm:"primaryKey"`
	Name        string `gorm:"type:varchar(200)"`
	Age         int
	StudentInfo StudentInfo `gorm:"ForeignKey:UserId;AssociationForeignKey:ID"`
}

type StudentInfo struct {
	UserId int
	ID     int    `gorm:"primaryKey"`
	IdCard string `gorm:"type:varchar(200)"`
}

func (StudentInfo) TableName() string {
	return "student_info"
}

var err error
var gormdb *gorm.DB

func main() {
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToOneDelete()
}

// 一对一关联删除
func OneToOneDelete() {
	var student Student = Student{}
	// 在这里要先查询再删除
	gormdb.Preload("StudentInfo").Find(&student, "ID = ?", 1)

	// 在这里要先删除外键关联的对象
	gormdb.Delete(&student.StudentInfo)
	// 再删除元对象
	gormdb.Delete(&student)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

一对多

一对多映射

作者和文章是一对多之间的关系

一个作者可以创作多个文章

其中Author结构体中包含一个Article的结构体

type Author struct {
	AID     int    `gorm:"primaryKey"`
	Name    string `gorm:"type:varchar(200)"`
	Age     int
	Article []Article `gorm:"ForeignKey:AuthorId;AssociationForeignKey:AID"`
}

type Article struct {
	ArtId    int    `gorm:"primaryKey"`
	Title    string `gorm:"type:varchar(200)"`
	Content  string `gorm:"type:varchar(200)"`
	AuthorId int    `gorm:"column:AuthorId"` // 外键
}

在Article字段后边加注解

ForeignKey表示外键 这里指向 Article.AuthorId字段

AssociationForeignKey表示关联外键 这里指向Author.AID字段

一对多添加

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Author struct {
	AID     int    `gorm:"primaryKey"`
	Name    string `gorm:"type:varchar(200)"`
	Age     int
	Article []Article `gorm:"ForeignKey:AuthorId;AssociationForeignKey:AID"`
}

type Article struct {
	ArtId    int    `gorm:"primaryKey"`
	Title    string `gorm:"type:varchar(200)"`
	Content  string `gorm:"type:varchar(200)"`
	AuthorId int    `gorm:"column:AuthorId"` // 外键
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	gormdb.AutoMigrate(Author{}, Article{})
	OneToManyAdd()
}

func OneToManyAdd() {
	var author Author = Author{
		Name: "海明威",
		Age:  21,
		Article: []Article{
			{
				Title:   "月子",
				Content: "内容",
			},
			{
				Title:   "伺候月子",
				Content: "内容",
			},
		},
	}

	gormdb.Create(&author)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

一对多查询

PreLoad方式
package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Author struct {
	AID     int    `gorm:"primaryKey"`
	Name    string `gorm:"type:varchar(200)"`
	Age     int
	Article []Article `gorm:"ForeignKey:AuthorId;AssociationForeignKey:AID"`
}

type Article struct {
	ArtId    int    `gorm:"primaryKey"`
	Title    string `gorm:"type:varchar(200)"`
	Content  string `gorm:"type:varchar(200)"`
	AuthorId int    `gorm:"column:AuthorId"` // 外键
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToOneSelect()
}

func OneToOneSelect() {
	var author Author = Author{}
	gormdb.Preload("Article").First(&author, 1)
	fmt.Println(author)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

输出结果为

{1 名称 12 [{1 母猪的产后护理 内容 1} {2 三个臭皮匠 内容 1}]}

一对多更新

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Author struct {
	AID     int    `gorm:"primaryKey"`
	Name    string `gorm:"type:varchar(200)"`
	Age     int
	Article []Article `gorm:"ForeignKey:AuthorId;AssociationForeignKey:AID"`
}

type Article struct {
	ArtId    int    `gorm:"primaryKey"`
	Title    string `gorm:"type:varchar(200)"`
	Content  string `gorm:"type:varchar(200)"`
	AuthorId int    `gorm:"column:AuthorId"` // 外键
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToManyUpdate()
}

// 先查询,再更新
func OneToManyUpdate() {
	var author Author = Author{}
	gormdb.Preload("Article").First(&author, 1)

	// 在这里更新这个作者下的所有文章,这里注意要加入where条件,否则就会全部更新掉
	gormdb.Model(&author.Article).Where("art_id", 1).Update("content", "更新之后的内容二")
	fmt.Println(author)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

输出

{1 海明威 21 [{1 月子 更新之后的内容二 1} {2 伺候月子 更新之后的内容二 1}]}

一对多删除

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Author struct {
	AID     int    `gorm:"primaryKey"`
	Name    string `gorm:"type:varchar(200)"`
	Age     int
	Article []Article `gorm:"ForeignKey:AuthorId;AssociationForeignKey:AID"`
}

type Article struct {
	ArtId    int    `gorm:"primaryKey"`
	Title    string `gorm:"type:varchar(200)"`
	Content  string `gorm:"type:varchar(200)"`
	AuthorId int    `gorm:"column:AuthorId"` // 外键
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	OneToManyDel()
}

// 先查询,再删除
func OneToManyDel() {
	var author Author = Author{}
	gormdb.Preload("Article").First(&author, 1)

	// 在这里删除这个作者下的所有文章,这里注意要加入where条件,否则就会全部删除掉
	gormdb.Model(&author.Article).Where("art_id = ?", 2).Delete(&author.Article)

}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

多对多

多对多映射

学生和课程之间是属于多对多的关系

一个学生可以学习多个课程

一个课程也可以被多个学生学习

type Student struct {
	ID   int    `gorm:"primaryKey"` // 主键
	SNo  int    // 学号
	Name string // 姓名
	Sex  string // 性别
	Age  int    // 年龄

	Cource []Cource `gorm:"many2many:Student2Cource"`
}

type Cource struct {
	ID          int    `gorm:"primaryKey"` // 课程主键
	CName       string // 课程名称
	TeacherName string // 老师名字
}

多对多添加

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Student struct {
	ID   int    `gorm:"primaryKey"` // 主键
	SNo  int    // 学号
	Name string // 姓名
	Sex  string // 性别
	Age  int    // 年龄

	Cource []Cource `gorm:"many2many:Student2Cource"`
}

type Cource struct {
	ID          int    `gorm:"primaryKey"` // 课程主键
	CName       string // 课程名称
	TeacherName string // 老师名字
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}

	gormdb.AutoMigrate(Student{}, Cource{})
	ManyToManyCreate()
}

// 多对多添加
func ManyToManyCreate() {
	var student Student = Student{
		SNo:  110,
		Name: "孙悟空",
		Sex:  "男",
		Age:  21,
		Cource: []Cource{
			{
				CName:       "java编程",
				TeacherName: "菩提老祖",
			},
			{
				CName:       "js编程",
				TeacherName: "唐僧",
			},
		},
	}

	gormdb.Create(&student)
}

// 获取数据库连接
func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...
}

自定义第三张表

有时候,中间表不是gorm自动生成

中间表除了两张表的id 可能还会存在其他字段

这时候描述两张表的多对多关系就会涉及到两个结构体标签

joinForeignKeyjoinReferences

下边的例子

一个人可以购买多个品牌的汽车

一个品牌的汽车也可以被多个人购买

以下例子是从Persion角度

定义了一个[]Cars的变量

joinForeignKey:PersionId 指向中间表的PersionId字段

joinReferences:CarId 指向中间表的CarId字段

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type Persion struct {
	ID   int    `gorm:"primaryKey;column:id"`         // 主键
	Name string `gorm:"type:varchar(20);column:name"` // 姓名
	Cars []Cars `gorm:"many2many:persion_car;joinForeignKey:PersionId;joinReferences:CarId;"`
}
func (Persion) TableName() string {
	return "pesions"
}
type PersionCar struct {
	PersionId int `gorm:"column:persion_id"` // 主键
	CarId     int `gorm:"column:car_id"`     // 主键
}
func (PersionCar) TableName() string {
	return "persion_car"
}
type Cars struct {
	ID      int       `gorm:"primaryKey;column:id"`         // 主键
	Name    string    `gorm:"type:varchar(20);column:name"` // 姓名
	Persion []Persion `gorm:"many2many:persion_car;joinForeignKey:CarId;joinReferences:persion_id;"`
}

func (Cars) TableName() string {
	return "cars"
}

var gormdb *gorm.DB

func main() {
	var err error
	gormdb, err = getCoon("root", "root", "192.168.10.20", "3306", "test")
	if err != nil {
		panic("failed to connect database")
	}
	Many2Many()

}

func Many2Many() {
	var Persion Persion
	gormdb.Preload("Cars").Debug().First(&Persion, 1)
	fmt.Println(Persion)
}

func getCoon(user string, password string, host string, port string, dbName string) (coon *gorm.DB, err error) {
	// ...获取连接对象
}

GROM结构体标签

gorm为键名的标签遵循GORM的解析规则,GORM支持如下tag,tag名大小写不敏感

标签项介绍
column指定 db 列名
type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
size指定列大小,ps:size:1024
primaryKey主键
unique指定为唯一
default默认值
precision精度
scale列大小
not nullNOT NULL
autoIncrement自动增长,不可与primaryKeytype同时使用否则不生效
autoIncrementIncrement自动步长,控制连续记录之间的间隔
embedded嵌套字段
embeddedPrefix嵌套字段前缀
autoCreateTime创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:milli
autoUpdateTime创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndexindex 相同,但创建的是唯一索引
check创建检查约束,例如 check:age > 13,查看 约束 获取详情
comment迁移时为字段添加注释
-忽略该字段,- 无读写权限

GROM中的事务

要在事务中执行一组操作,一般流程如下

// 开始事务
tx := db.Begin()

// 在事务中做一些数据库操作(从这一点使用'tx',而不是'db')
tx.Create(...)

// ...

// 发生错误时回滚事务
tx.Rollback()

// 或提交事务
tx.Commit()
 func CreateAnimals(db *gorm.DB) err {
      tx := db.Begin()
     // 注意,一旦你在一个事务中,使用tx作为数据库句柄

     if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
         tx.Rollback()
         return err
     }

     if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
         tx.Rollback()
         return err
     }

     tx.Commit()
     return nil
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值