Gorm学习(五)进阶:多态关联、关联标签以及事务
目录
- 前言
- 一、多态关联
- 1、多态关联概述
- 2、为什么用多态关联?
- 3、Has One
- 4、Has Many
- 二、关联标签
- 1、polymorphic & polymorphicValue
- 2、foreignKey & references
- 3、Many to Many
- 4、joinForeignKey & joinReferences
- 三、事务
- 1、事务概述
- 2、事务操作
- 1)回滚
- 2)嵌套事务
- 3、手动事务
- 4、禁用默认事务
- 四、小结
前言
感谢开源项目gin-vue-admin,以及1010工作室的视频教程
本人学识尚浅,如有错误,请评论指出,谢谢!
详细可见个人博客:https://linzyblog.netlify.app/
一、多态关联
1、多态关联概述
什么是多态?
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
什么是多态表?
假设我们有一张地址表,其中的地址可能是来自User中的,也可能是来自Orders中的。而区分不同的对象则用type字段。如:type=User时对象是文章表。
- 什么是多态关联?
多态关联就是多态表和对象表之间的关联性。一个多态关联由使用同一外键发生的两个(或多个)关联组成.
2、为什么用多态关联?
出现需要外键引用多个表的情况,不可能删除原来表结构,重新添加一个外键ID再建表,所以我们可以建立一个交叉表。让Addres不再依赖于User表或者Order表。
has one
的情况解决方案,如果我们希望一个给定的地址,只能够在一张交叉表中出现一次,上面的复合主键已经做到了。
has many
的情况解决方案,如果希望一个地址可以在一张交叉表中出现多次,可以取消Address的复合主键。
3、Has One
GORM 为 has one
和 has many
提供了多态关联支持,它会将拥有者实体的表名、主键值都保存到多态类型的字段中。
type User struct {ID intName string//polymorphic指定多态类型,比如模型名Address Address `gorm:"polymorphic:Owner;"`
}type Order struct {ID intName stringAddress Address `gorm:"polymorphic:Owner;"`
}type Address struct {ID intName stringOwnerID intOwnerType string
}func main() {db.AutoMigrate(&User{}, &Order{}, &Address{})
}
- 创建记录
db.Create(&User{Name: "linzy",Address: Address{Name: "翻斗花园",},
})
db.Create(&Order{Name: "忘崽牛奶",Address: Address{Name: "火星幼儿园",},
})
owner_type就是关联的那张表。
owner_id就是关联的表的主键。
4、Has Many
type User struct {ID intName stringAddress []Address `gorm:"polymorphic:Owner;"`
}type Order struct {ID intName stringAddress Address `gorm:"polymorphic:Owner;"`
}type Address struct {ID intName stringOwnerID intOwnerType string
}func main() {db.AutoMigrate(&User{}, &Order{}, &Address{})db.Create(&User{Name: "linzy",Address: []Address{{Name: "翻斗花园"},{Name: "杭州西湖"},},})
}
二、关联标签
1、polymorphic & polymorphicValue
- polymorphic:通俗讲用来指定id与type的前缀。
- polymorphicValue用来告诉关联表我是谁,默认都是表名。
type User struct {ID intName string//polymorphic:通俗讲用来指定id与type的前缀Address []Address `gorm:"polymorphic:Address;"`
}type Order struct {ID intName string//polymorphicValue用来告诉关联表我是谁,默认都是表名Address Address `gorm:"polymorphic:Address;polymorphicValue:master"`
}type Address struct {ID intName stringAddressID intAddressType string
}func main() {db.AutoMigrate(&User{}, &Order{}, &Address{})db.Create(&User{Name: "linzy",Address: []Address{{Name: "翻斗花园"},{Name: "杭州西湖"},},})db.Create(&Order{Name: "忘崽牛奶",Address: Address{Name: "火星幼儿园",},})
}
2、foreignKey & references
- foreignKey:用来指定连接表的外键。
- references:用来指定引用表的列名与连接表的外键映射。
GORM里默认是连接表和引用表的主键来作为做外键以及外键映射的。
Has One的例子:
type CreditCard struct {gorm.ModelNumber string//外键指向CreditCardNumberInfo Info `gorm:"foreignKey:CreditCardNumber"`
}type Info struct {ID uintName stringAge intCreditCardNumber string
}func main() {db.AutoMigrate(&CreditCard{}, &Info{})db.Create(&CreditCard{Number: "123456",Info: Info{Name: "linzy",Age: 18,},})db.Create(&CreditCard{Number: "456789",Info: Info{Name: "slyyy",Age: 66,},})
}
注意:credit_card_number并没有自动指向creditcard表里的number字段,他还是会默认指向引用表里的主键,所以在用
foreignKey
的时候最好类型相同或者使用references
搭配使用。
type CreditCard struct {ID uint//设置唯一和固定长度Number string `gorm:"index:unique;size:255"`Info Info `gorm:"foreignKey:CreditCardNumber;references:Number"`
}type Info struct {ID uintName stringAge int//设置唯一和固定长度CreditCardNumber string `gorm:"index:unique;size:255"`
}func main() {db.AutoMigrate(&CreditCard{}, &Info{})db.Create(&CreditCard{ID: 1,Number: "123456",Info: Info{Name: "linzy",Age: 18,},})db.Create(&CreditCard{ID: 2,Number: "456789",Info: Info{Name: "slyyy",Age: 66,},})
}
注意: 某些数据库只允许在唯一索引字段上创建外键,如果在迁移时会创建外键,则需要指定
index:unique
标签。
错误:
Error 1170: BLOB/TEXT column 'credit_card_number' used in key specification without a key length
出现这个问题是因为你的外键或者外键映射的字段是text类型也就是不固定长度string类型,不能作为外键或外键映射,必须通过标签size
设置固定长度。Error 1215: Cannot add foreign key constraint
这个错误是不能创建外键,主要原因可能是你外键映射的字段不是引用表的主键,建议标签设置为唯一index:unique
。
3、Many to Many
type CreditCard struct {ID uintNumber string `gorm:"index:unique;size:255"`Infos []Info `gorm:"many2many:card_infos;foreignKey:Number;references:Name;"`
}type Info struct {ID uintName string `gorm:"index:unique;size:255"`Age int
}func main() {db.AutoMigrate(&CreditCard{}, &Info{})db.Create(&CreditCard{Number: "123456",Infos: []Info{{ID: 1,Name: "linzy",Age: 18,},},})db.Create(&CreditCard{Number: "456789",Infos: []Info{{ID: 2,Name: "slyyy",Age: 66,},{ID: 3,Name: "qhgwueiq",Age: 1,},},})
}
注意:在
Many to Many
的情况下,foreignKey指向的是引用表的外键映射字段
,references指向的是关联表的外键字段
,一定不要搞混了。
4、joinForeignKey & joinReferences
- joinForeignKey:指定
Many to Many
产生的连接表中关联外键映射字段的名称。 - joinReferences:指定
Many to Many
产生的连接表中关联外键字段的名称。
type CreditCard struct {ID uintNumber string `gorm:"index:unique;size:255"`Infos []Info `gorm:"many2many:card_infos;foreignKey:Number;joinForeignKey:card_number;references:Name;joinReferences:name"`
}type Info struct {ID uintName string `gorm:"index:unique;size:255"`Age int
}func main() {db.AutoMigrate(&CreditCard{}, &Info{})db.Create(&CreditCard{Number: "123456",Infos: []Info{{ID: 1,Name: "linzy",Age: 18,},},})db.Create(&CreditCard{Number: "456789",Infos: []Info{{ID: 2,Name: "slyyy",Age: 66,},{ID: 3,Name: "qhgwueiq",Age: 1,},},})
}
三、事务
1、事务概述
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列
,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。 事务由事务开始与事务结束之间执行的全部数据库操作组成。
数据库事务必须具备的四个特性:
- 原子性(Atomicity)
事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。 - 一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 - 隔离性(Isolation)
一个事务的执行不能被其他事务干扰。 - 持续性/永久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
2、事务操作
要在事务中执行一系列操作,一般流程如下:
type User struct {gorm.ModelName string
}func main() {db.AutoMigrate(&User{})db.Transaction(func(tx *gorm.DB) error {// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而不是 'db')if err := tx.Create(&User{Name: "Giraffe"}).Error; err != nil {// 返回任何错误都会回滚事务return err}if err := tx.Create(&User{Name: "Lion"}).Error; err != nil {return err}// 返回 nil 提交事务return nil})
}
1)回滚
注意:返回任何错误都会回滚事务。回滚则事务内的操作一律不执行。
func main() {db.AutoMigrate(&User{})db.Transaction(func(tx *gorm.DB) error {// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而不是 'db')if err := tx.Create(&User{Name: "linzy"}).Error; err != nil {// 返回任何错误都会回滚事务return err}if err := tx.Create(&User{Name: "slyyy"}).Error; err != nil {return err}if true {return errors.New("回滚")}// 返回 nil 提交事务return nil})
}
2)嵌套事务
嵌套事务的作用在于较大的事务中,你只想回滚一部分操作,例如你去银行转账,已经通过银行卡号和密码登录了,转账的过程是你的银行账户扣去多少钱,同时别人的银行账户加上多少钱,如果中途发生错误,需要回滚,应该回滚到你账号登录后的状态,而不是直接回滚到你账号登录前。
type User struct {gorm.ModelName stringNumber int
}func main() {db.AutoMigrate(&User{})db.Transaction(func(tx *gorm.DB) error {tx.Create(&User{Name: "linzy",Number: 100,})tx.Create(&User{Name: "slyyy",Number: 100,})fmt.Println("登陆后")tx.Transaction(func(tx2 *gorm.DB) error {// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而不是 'db')if err := tx2.Model(&User{}).Where("name = ?", "linzy").Update("number", 80).Error; err != nil {// 返回任何错误都会回滚事务return err}if err := tx2.Model(&User{}).Where("name = ?", "slyyy").Update("number", 120).Error; err != nil {return err}return nil})fmt.Println("转账结束")// 返回 nil 提交事务return nil})
}
正常的过程应该是这样,如果说嵌套事务发生回滚操作之后的情况呢?
func main() {db.AutoMigrate(&User{})db.Transaction(func(tx *gorm.DB) error {tx.Create(&User{Name: "linzy",Number: 100,})tx.Create(&User{Name: "slyyy",Number: 100,})fmt.Println("登陆后")tx.Transaction(func(tx2 *gorm.DB) error {// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而不是 'db')if err := tx2.Model(&User{}).Where("name = ?", "linzy").Update("number", 80).Error; err != nil {// 返回任何错误都会回滚事务return err}if true {fmt.Println("转账失败,对面是骗子不能转!!")return errors.New("转账失败")}if err := tx2.Model(&User{}).Where("name = ?", "slyyy").Update("number", 120).Error; err != nil {return err}return nil})fmt.Println("转账结束")// 返回 nil 提交事务return nil})
}
3、手动事务
Gorm 支持直接调用事务控制方法(commit、rollback),例如:
事务方法 | 说明 |
---|---|
tx := db.Begin() | 开始事务 |
tx.(db操作) | 在事务中执行一些 db 操作 |
tx.Rollback() | 遇到错误时回滚事务 |
tx.SavePoint() | 设置保存点标记 |
tx.RollbackTo() | 回滚到保存点标记 |
tx.Commit() | 提交事务 |
type User struct {gorm.ModelName stringNumber int
}func main() {db.AutoMigrate(&User{})// 开始事务tx := db.Begin()// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')tx.Create(&User{Name: "linzy",Number: 100,})tx.Create(&User{Name: "slyyy",Number: 100,})fmt.Println("登陆后")//设置回滚标记tx.SavePoint("登录了")flag := true{if err := tx.Model(&User{}).Where("name = ?", "linzy").Update("number", 80).Error; err != nil {flag = true}if err := tx.Model(&User{}).Where("name = ?", "slyyy").Update("number", 120).Error; err != nil {flag = true}//出现问题了得写在一系列事务之后进行回滚if flag {fmt.Println("转账失败,对面是骗子不能转!!")//回滚到指定标记tx.RollbackTo("登录了")}}// 遇到错误时回滚事务fmt.Println("转账结束")// 否则,提交事务tx.Commit()
}
官方示例:
func CreateAnimals(db *gorm.DB) error {// 再唠叨一下,事务一旦开始,你就应该使用 tx 处理数据tx := db.Begin()//延迟函数一定要写上,因为出现panic错误时事务可能没办法回滚,需要手动再回滚defer func() {if r := recover(); r != nil {tx.Rollback()}}()if err := tx.Error; err != nil {return err}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}return tx.Commit().Error
}
4、禁用默认事务
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
// 全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{SkipDefaultTransaction: true,
})// 持续会话模式
tx := db.Session(&Session{SkipDefaultTransaction: true})
tx.First(&user, 1)
tx.Find(&users)
tx.Model(&user).Update("Age", 18)
四、小结
本章拓展了GORM对数据库的更多支持,关联标签与事务是很重要的内容。
- 关联标签:
标签 | 描述 |
---|---|
foreignKey | 指定当前模型的列作为连接表的外键 |
references | 指定引用表的列名,其将被映射为连接表外键 |
polymorphic | 指定多态类型,比如模型名 |
polymorphicValue | 指定多态值、默认表名 |
many2many | 指定连接表表名 |
joinForeignKey | 指定连接表的外键列名,其将被映射到当前表 |
joinReferences | 指定连接表的外键列名,其将被映射到引用表 |
- 事务:
手动事务适用于小事务操作,出错了直接全部回滚会更好,虽然提供了 SavePoint
、Rollbackto
方法,来提供保存点以及回滚至保存点功能,但是有一些同步操作操作很不方便。
GORM自带事务适用大事务操作,可以使用嵌套事务。
若有写的错误的或者需要改进的地方,希望能直接指出,再次感谢GVA淼哥的教程!
Gorm学习(五)进阶:多态关联、关联标签以及事务相关推荐
- Gorm学习(四)基础:关联
目录 前言 一.One To One 一对一 1.Belongs To 属于 1)创建记录 2)查询记录 3)重写外键 4)重写引用 5)关联模式 a.查找关联 b.删除关联 c.添加关联 d.修改( ...
- (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射
http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...
- dedecms 漏洞_织梦dedecms文档内容页自动关联tag标签加入内链的方法_dedecms_CMS教程...
效果: 实现教程 1.后台-系统-核心设置-关键字替换,选择[是] 2.后台-系统-其他选项-关键词替换次数,填[1]或者[0] 1:表示文档内容里有多个关键词,只让1个是内链 0:表示文档内容里有多 ...
- tcga癌症亚型获取_将亚型多态性与通用多态性相关联的危险
tcga癌症亚型获取 Java 5已将通用多态性引入Java生态系统. 即使我们都知道由于泛型类型擦除及其后果而引起的无数警告,这还是对Java语言的重要补充. 通用多态性(也称为参数多态性 )通常与 ...
- 将亚型多态性与通用多态性相关联的危险
Java 5已将通用多态性引入Java生态系统. 即使我们都知道由于泛型类型擦除及其后果而引起的无数警告,这对Java语言还是一个很大的补充. 通用多态性(也称为参数多态性 )通常与可能预先存在的亚型 ...
- JavaScript学习(九十二)—关联数组的基本操作
JavaScript学习(九十二)-关联数组的基本操作 王同学的每天进步一点点系列!!! 一.关联数组的定义 定义:所谓关联数组,就是指数组元素的下标为字符型 二.关联数组的创建方式 1)定义一个空数 ...
- RTKLIB专题学习(五)---单点定位实现进阶(一)
RTKLIB专题学习(五) 今天我们一起来了解一下,RTKLIB中的单点定位是如何实现的: 简单来说,就是最小二乘法,但是呢,RTKLIB里面的最小二乘实际上是加权最小二乘,因为他给出了观测值的权(实 ...
- Laravel大型项目系列教程(五)之文章和标签管理
Laravel大型项目系列教程(五)之文章和标签管理 本节教程将大概完成文章和标签管理. 1.文章管理 首先创建管理后台文章列表视图: $ php artisan generate:view admi ...
- Docker学习五:Docker 数据管理
前言 本次学习来自于datawhale组队学习: 教程地址为: https://github.com/datawhalechina/team-learning-program/tree/master/ ...
最新文章
- 消除图片在ie中缓存而无法更新的问题
- c# 读hex_c#十六进制到位转换(c# hex to bit conversion)
- 面向行人重识别的局部特征研究进展、挑战与展望
- SAP CRM WebClient UI,点击Master Data工作中心后执行的JavaScript代码
- leetcode -39组合总数
- 一篇文章带你快速理解微服务架构,由浅入深带你走进微服务架构的核心
- python能自学成功吗-想自学Python,如何才能坚持下来?
- 【教程】油猴脚本开发入门教程
- StringUtil工具类之去除所有的空白字符
- Chrome扩展之书签
- 数学建模之排队论模型及代码
- 图片转excel方法
- php爬虫框架选用什么
- Android 接入阿里云推送com.aliyun.ams:alicloud-android-push:3.7.4步骤(二)
- 四大行、三大运营商在列,或有15家公司参与央行数字货币
- 数据分析常用图表常用场景
- 个体工商户属于小微企业吗_个体户属于小微企业吗?
- 特此感谢!酷睿12、希捷硬盘、机械键盘……免费送大家
- 自制android摇一摇闹钟,摇一摇闹钟
- 哈工大计算机网络Mooc 最后的总结
热门文章
- arm上使用tcpdump抓包
- google app engine for java 的web应用程序
- 华为无线二层网络配置案例(直接转发)
- java核心卷pdf及代码分享
- @vue/cli 安装缓慢问题
- HTMLCSS相关面试题
- vue常用修饰符 v-moble后面或者 @后面
- 基于RFID技术的物流货物分拣管理应用
- 清华大学计算机系哪个专业就业前景最好,清华大学有3个专业,是世界第一,实力和就业前景都很棒...
- 关于百度旧博客http://hi.baidu.com/forverlin1204/blog