快速开始
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{}
,此时别忘了指定 Model
或 Table
,例如:
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 可能还会存在其他字段
这时候描述两张表的多对多关系就会涉及到两个结构体标签
joinForeignKey
和 joinReferences
下边的例子
一个人可以购买多个品牌的汽车
一个品牌的汽车也可以被多个人购买
以下例子是从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 null 、size , autoIncrement … 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT |
size | 指定列大小,ps:size:1024 |
primaryKey | 主键 |
unique | 指定为唯一 |
default | 默认值 |
precision | 精度 |
scale | 列大小 |
not null | NOT NULL |
autoIncrement | 自动增长,不可与primaryKey 、type 同时使用否则不生效 |
autoIncrementIncrement | 自动步长,控制连续记录之间的间隔 |
embedded | 嵌套字段 |
embeddedPrefix | 嵌套字段前缀 |
autoCreateTime | 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,可以使用 nano /milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:milli |
autoUpdateTime | 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,可以使用 nano /milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli |
index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 |
uniqueIndex | 与 index 相同,但创建的是唯一索引 |
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
}