一、概述
1.1、ORM简介
对象关系映射模式(ORM)是一种为了解决面向对象与关系数据库(如mysql数据库)存在的互不匹配的现象的技术。简单来说,ORM十通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
1.2、安装
在goland中引入,然后安装就可以啦
"gorm.io/driver/mysql"
"gorm.io/gorm"
1.3、相关操作
1.3.1、创建一个结构体---整个过程的前提
注意gorm.Model
type Product struct {
gorm.Model //属性:ID、CreateTime、UpdateTime、DeleteTime
Code string
Price uint
}
1.3.2、建立连接
dsn := "root:123456@tcp(127.0.0.1:3306)/go_study?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
1.3.3、 创建表
db.AutoMigrate(&Product{})
func create(db *gorm.DB) {
//创建表 把结构体创建表直接到数据库里面
db.AutoMigrate(&Product{}) //表的名称为products
}
1.3.4、插入数据
db.Create(&p)
// 插入数据-插入一整行
func insert(db *gorm.DB) {
p := Product{
Code: "Q1002",
Price: 30,
}
result := db.Create(&p)
fmt.Printf("影响的行数:%v\n", result.RowsAffected)
fmt.Printf("p.id:%v\n", p.ID)
/*
影响的行数:1
p.id:3
*/
}
// 插入数据--只插入某些列
func insert2(db *gorm.DB) {
p := Product{
Code: "Q1003",
Price: 60,
}
result := db.Select("Code", "CreatedAt").Create(&p)
//只往Code和CreatedAt列中插入了信息
fmt.Printf("影响的行数:%v\n", result.RowsAffected)
fmt.Printf("p.id:%v\n", p.ID)
/*
影响的行数:1
p.id:4
*/
}
// 插入数据--批量插入
func insert3(db *gorm.DB) {
var p = []Product{
{Code: "Q1004"}, {Code: "Q1005"}, {Code: "Q1006"}}
db.Create(&p)
}
1.3.5、查询
- db.First(&product, 1)
- db.First(&product, "code=?", "1001")
// 查询
func find1(db *gorm.DB) {
var product Product
//获取第一条记录--主键升序
db.First(&product)
fmt.Printf("product.id:%v\n", product.ID)
//获取第一条记录--无指定顺序
db.Take(&product)
fmt.Printf("product.id:%v\n", product.ID)
//获取最后一条记录
db.Last(&product)
fmt.Printf("product.id:%v\n", product.ID)
//根据条件来查询
db.First(&product, "code=?", "1001")
fmt.Printf("find result:%v\n", product)
}
// 根据主键检索
func find2(db *gorm.DB) {
var product Product
//根据主键查询
db.First(&product, 3) //&product是指将查询到的结果放在product中。1是指查询主键为1的值
fmt.Printf("find result ID:%v\n", product.ID)
//根据主键批量查询
var products []Product
db.Find(&products, []int{1, 2, 3})
for _, p := range products {
fmt.Printf("find result ID:%v\n", p.ID)
}
}
// 检索全部对象
func find3(db *gorm.DB) {
var products []Product
result := db.Find(&products)
fmt.Printf("查了%v行\n", result.RowsAffected)
}
条件查询
更多查看gorm查询记录
1.3.6、更新
- db.Model(&product).Update("Price", 200)
- db.Model(&product).Updates(Product{Price: 300, Code: "F003"})
- db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F003"})
func update(db *gorm.DB) {
//更新之前首先要先查询,查出来。可以根据主键或者条件来查询
var product Product
db.First(&product, 1)
//查出来之后,将价格改成200
db.Model(&product).Update("Price", 200)
//更新多个属性的数据
//db.Model(&product).Updates(Product{Price: 300, Code: "F003"})
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F003"})
}
1.3.7、 删除
db.Delete(&product, 2) 不是物理删除,是软删除,是添加了一个删除标记。在deleteTime字段
// 删除 --不是物理删除,是软删除,是添加了一个删除标记。再deleteTime字段
func del(db *gorm.DB) {
var product Product
db.First(&product, 2) //删除之前先找到
db.Delete(&product, 2)
}
示例代码
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type Product struct {
gorm.Model //属性:ID、CreateTime、UpdateTime、DeleteTime
Code string
Price uint
}
// 创建表
func create(db *gorm.DB) {
//创建表 把结构体创建表直接到数据库里面
db.AutoMigrate(&Product{}) //表的名称为products
}
// 插入数据
func insert(db *gorm.DB) {
p := Product{
Code: "1002",
Price: 1000,
}
db.Create(&p)
}
// 查询
func find(db *gorm.DB) {
var product Product
//根据主键查询
db.First(&product, 1) //&product是指将查询到的结果放在product中。1是指查询主键为1的值
fmt.Printf("find result:%v\n", product)
//根据条件来查询
db.First(&product, "code=?", "1001")
fmt.Printf("find result:%v\n", product)
}
// 更新
func update(db *gorm.DB) {
//更新之前首先要先查询,查出来。可以根据主键或者条件来查询
var product Product
db.First(&product, 1)
//查出来之后,将价格改成200
db.Model(&product).Update("Price", 200)
//更新多个属性的数据
//db.Model(&product).Updates(Product{Price: 300, Code: "F003"})
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F003"})
}
// 删除 --不是物理删除,是软删除,是添加了一个删除标记。再deleteTime字段
func del(db *gorm.DB) {
var product Product
db.First(&product, 2) //删除之前先找到
db.Delete(&product, 2)
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_study?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
//find(db)
//update(db)
del(db)
}
二、gorm声明模型
type Product struct {
gorm.Model //属性:ID、CreateTime、UpdateTime、DeleteTime
Code string
Price uint
}
Grom更倾向于约定,而不是配置
gorm.Model定义
1.4、原生SQL和SQL构造器
func testRow1() {
type Result struct {
ID int
Name string
Age int
}
var result Result
//注意要指定运行结果的存放结构,例如这里的result和下面的age
db.Raw("select id,name,age from users where name=?", "tom").Scan(&result)
fmt.Printf("result:%v\n", result) //result:{1 tom 11}
var age int
db.Raw("select sum(age) from users").Scan(&age) //age:23 把找到的东西放在age里面
fmt.Printf("age:%v\n", age)
}
func testRaw2() {
db.Exec("update users set age=? where name=?", 100, "tom") //执行一个更新
}
// 命名参数
func testRaw3() {
var user User
db.Where("name=@myname", sql.Named("myname", "tom")).Find(&user)
fmt.Printf("%v\n", user)
}
func testRaw4() {
var name string
var age int
row := db.Table("users").Where("name=?", "tom").Select("name", "age").Row()
row.Scan(&name, &age)
fmt.Printf("name:%v\n", name)
fmt.Printf("age:%v\n", age)
rows, _ := db.Model(&User{}).Where("age>?", 18).Select("name", "age").Rows()
for rows.Next() {
rows.Scan(&name, &age)
fmt.Printf("name:%v\n", name)
fmt.Printf("age:%v\n", age)
}
}
1.5、关联关系
1.5.1、Belongs To
多对一关系
type Email struct {
gorm.Model
Email string
UserNewID uint
}
type Language struct {
gorm.Model
Name string
UserNew []*UserNew `gorm:"many2many:user_news_languages"`
}
type Address struct {
gorm.Model
Address1 string
UserNewID uint
}
type UserNew struct {
gorm.Model
Name string
BillingAddress Address
ShippingAddress Address
Emails []Email
Languages []Language `gorm:"many2many:user_news_languages"`
}
func create() {
db.AutoMigrate(&UserNew{}, &Email{}, &Address{}, &Language{})
}
func test1() {
user := UserNew{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "[email protected]"},
{Email: "[email protected]"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
//db.Create(&user)
//如果使用的不是create,而是update,就要用下面这句话
db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
}
// 关联的查询
func test2() {
var user UserNew
//First拿到第一个用户
db.First(&user)
var lan []Language
db.Model(&user).Association("Languages").Find(&lan)
fmt.Printf("languages:%v\n", lan)
}
// 关联计数
func test3() {
var user UserNew
//First拿到第一个用户
db.First(&user)
count := db.Model(&user).Association("Languages").Count()
fmt.Printf("Count:%v\n", count)
}
1.5.2、has one
has one 与另一个模型建立一对一的关联,但是它和一对一关系有些许不同。这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例
// has to关系,一对一 一个卡片只能有一个主人
func test1() {
type CreditCard struct {
gorm.Model
Number string
UserNewID uint //注意这里一定是UserNewID,如果是别的,或者写错成User,就不会创建成功
//UserNewID是外键,对应UserNew中的ID
}
type UserNew struct {
gorm.Model
CreditCard CreditCard
}
db.AutoMigrate(&UserNew{}, &CreditCard{}) //注意创建关系,分前后
}
多态关联
Gorm为has one和has many提供了多态关联支持,它将拥有者实体的表明、主键值都保存到多态类型的字段中
// has to关系,一对一 一个卡片只能有一个主人
func test1() {
type CreditCard struct {
gorm.Model
Number string
UserNewID uint //注意这里一定是UserNewID,如果是别的,或者写错成User,就不会创建成功
//UserNewID是外键,对应UserNew中的ID
}
type UserNew struct {
gorm.Model
CreditCard CreditCard
}
db.AutoMigrate(&UserNew{}, &CreditCard{}) //注意创建关系,分前后
}
// 多态关联
func test2() {
db.AutoMigrate(&Toy{}, &Cat{}, &Dog{})
}
func test3() {
db.Create(&Dog{Name: "dog1", Toy: Toy{Name: "toy1"}})
db.Create(&Cat{Name: "cat1", Toy: Toy{Name: "toy2"}})
}
最后的结果:
在向toy中添加时,也会自动添加到dog表和cat表中
1.5.3、has many
https://mp.weixin.qq.com/s/i5R_WKpPkak0F8jcg3jnfw
has many与另一个模型建立了一对多的连接。不同于has one,拥有者可以有零或者多个关联模型
例如,每个user可以有多张credit card
type Language struct {
gorm.Model
Name string
}
type UserNew struct {
gorm.Model
Languages []Language `gorm:"many2many:user_new_languages"`
}
func test1() {
db.AutoMigrate(&Language{}, &UserNew{})
//最后创建了三张表,分别是language、usernew、user_new_languages(中间表--userid、languagesid)
//当我们添加记录时,会自动往中间表中添加
}
func insert() {
l := Language{
Name: "English",
}
l2 := Language{
Name: "chinese",
}
//创建用户时要指定语言
user := &UserNew{
Languages: []Language{l, l2},
}
db.Create(&user)
db.Create(&l)
}
数据库:
1.5.3、多对多 many to many
many to many会在两个model中添加一张连接表
例如,应用里面包含了user和language,且一个user可以说多种language,多个user也可以说一种language
type Language struct {
gorm.Model
Name string
}
type UserNew struct {
gorm.Model
Languages []Language `gorm:"many2many:user_new_languages"`
}
func test1() {
db.AutoMigrate(&Language{}, &UserNew{})
//最后创建了三张表,分别是language、usernew、user_new_languages(中间表--userid、languagesid)
//当我们添加记录时,会自动往中间表中添加
}
func insert() {
l := Language{
Name: "English",
}
l2 := Language{
Name: "chinese",
}
//创建用户时要指定语言
user := &UserNew{
Languages: []Language{l, l2},
}
db.Create(&user)
db.Create(&l)
}
数据库结果;
1.5.4、实体关联
type Email struct {
gorm.Model
Email string
UserNewID uint
}
type Language struct {
gorm.Model
Name string
UserNew []*UserNew `gorm:"many2many:user_news_languages"`
}
type Address struct {
gorm.Model
Address1 string
UserNewID uint
}
type UserNew struct {
gorm.Model
Name string
BillingAddress Address
ShippingAddress Address
Emails []Email
Languages []Language `gorm:"many2many:user_news_languages"`
}
func create() {
db.AutoMigrate(&UserNew{}, &Email{}, &Address{}, &Language{})
}
func test1() {
user := UserNew{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "[email protected]"},
{Email: "[email protected]"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
//db.Create(&user)
//如果使用的不是create,而是update,就要用下面这句话
db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
}
更新后数据库的表现:
可见,虽然只更新了usernew,但是与之关联的表全都发生了改变
1.6、gorm会话--session
1.7、gorm事务
确保数据的一致性。Gorm在事务里面执行写入操作。要么都成功、要么都失败
package main
import (
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
var db *gorm.DB
func test1() {
//session级别的禁用事务
db.Session(&gorm.Session{SkipDefaultTransaction: true})
}
type User struct {
gorm.Model
Name string
}
// c测试事务操作--没有使用事务控制,所以user会添加成功。nil添加失败
func test2() {
user := User{
Name: "tom",
}
db.Create(&user)
db.Create(nil) //肯定失败
}
// 测试事务操作---使用事务控制,都没有添加成功--手动控制
func test3() {
user := User{
Name: "kite",
}
//手动控制
tx := db.Begin() //有事务控制的db
tx.Create(&user)
err := tx.Create(nil).Error
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}
func test4() {
user := User{
Name: "tony",
}
//自动完成Rollback或者commit
db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
if err := tx.Create(nil).Error; err != nil {
return err
}
return nil
})
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_study?charset=utf8mb4&parseTime=True&loc=Local"
//SkipDefaultTransaction: true 禁用全局事务。这样事务就不生效了
d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ /*SkipDefaultTransaction: true*/ })
if err != nil {
log.Fatal(err)
}
db = d
//test2()
//test3()
test4()
}