gorm
作者:互联网
转载请注明出处!!!http://www.he10.top/article/detail/51
博主一直是写python的,面对GO语言真是爱恨交加,它虽能够弥补python运行中的性能缺陷,但也给你带来从动态语言到静态语言的种种不适。
本文主要讲使用gorm中遇到的各种问题以及解决问题的思路、理解。如果你是想更彻底的了解gorm你得需要看下官网文档。请移步:gorm中文文档
gorm不如python sqlalchemy或Django的ORM封装的那么彻底,使用中能接触到更直接的操作mysql的东西,有弊也有利。
一、定义模型类
gorm提供了默认的模型类gorm.Model,它里面有4个字段,分别是:ID、CreateAt、UpdateAt、DeleteAt;ID为自增主键(定义的模型类中如有ID字段,默认为自增主键),CreateAt、UpdateAt、DeleteAt分别对应创建时间、更新时间、删除时间(软删除),如果你在定义的模型类中使用了gorm.Model,那么在Create、Update、Delete时会分别给他们赋值,像python的orm了,但这三个字段是不能挑选的,也就是说你想要用自动的赋值时间,你就必须这三个字段全都要。嗯,挺人性化的~
由于我是老项目迭代,表字段都定义好了,没有用到DeleteAt,所以自定义了一个字段类型用于表中的时间字段。下面主讲自定义时间字段、枚举字段和外键,因为gorm定义字段可以通过type一一对应mysql字段,所以其他类型字段没什么好讲的了
1.1 自定义时间字段类型
自定义字段需要定义Scan和Value两个方法接口,分别对应 从数据库中取出数据后的对应操作 和 将数据写入数据库的对应操作。在写后端接口时,如通过json传数据给前端,还可以定义MarshalJSON方法接口,这样就可以在字段值转json时自动化了
本地定义LocalDateTime结构体,如下:
1 package utils 2 3 import ( 4 "database/sql/driver" 5 "errors" 6 "fmt" 7 "time" 8 ) 9 10 type LocalDateTime time.Time 11 12 func (t LocalDateTime) MarshalJSON() ([]byte, error) { 13 // 默认情况给前端的时间格式 %Y-%m-%d 14 tTime := time.Time(t) 15 tStr := tTime.Format("2006-01-02") 16 return []byte(fmt.Sprintf("\"%v\"", tStr)), nil 17 } 18 19 func (t LocalDateTime) Value() (driver.Value, error) { 20 // 这里定义将数据写入数据库的对应操作,也就是将自定义的LocalDateTime数据类型如何转化成数据库中字段的数据类型 21 tTime := time.Time(t) 22 return tTime.Format("2006-01-02 15:04:05.000000"), nil 23 } 24 25 func (t *LocalDateTime) Scan(v interface{}) error { 26 // 这里定义从数据库中取出数据后的对应操作,也就是从数据库中取出到的数据类型如何转化为LocalDateTime的数据类型 27 switch vt := v.(type) { 28 case []byte: 29 tTime, _ := time.Parse("2006-01-02 15:04:05.000000", string(vt)) 30 *t = LocalDateTime(tTime) 31 case string: 32 tTime, _ := time.Parse("2006-01-02 15:04:05.000000", vt) 33 *t = LocalDateTime(tTime) 34 default: 35 return errors.New("类型处理错误") 36 } 37 return nil 38 } 39 40 func (t LocalDateTime) ParseDateTime() string { 41 // 特殊情况给到前端时间格式 %Y-%m-%d %H:%M 42 tTime := time.Time(t) 43 44 return tTime.Format("2006-01-02 15:04") 45 }
模型类中使用
1 type BaseModel struct { 2 ID uint `gorm:"type:int(11) auto_increment;not null;primaryKey;" json:"id"` 3 CreateTime utils.LocalDateTime `gorm:"type:datetime(6);not null;column:create_time" json:"create_time"` 4 UpdateTime utils.LocalDateTime `gorm:"type:datetime(6);not null;column:update_time" json:"update_time"` 5 IsDelete bool `gorm:"type:bool;not null;column:is_delete" json:"is_delete"` 6 }
值得一题:
Value方法需要定义LocalDateTime为值类型,因为Go底层是通过值类型调用方法的,传入若是指针类型则将拿不到指针类型的方法,从而报 sql: converting argument $1 type 错误。Value方法对应写数据(增改),也就是说发生增改操作是会来调用该方法。
Scan方法需要定义LocalDateTime为指针类型,因为在拿到数据库数据后赋值给LocatDateTime对象,需要为LocalDateTime对象的指针才能正确赋值。Scan方法对应读数据,也就是发生读取操作时回来调用该方法。
1.2 枚举类型
枚举类型需要自定义一个基本数据类型(string、int64【网上看到用int插入数据时可能会报错,用int64不会,未实测】等),然后通过该数据类型定义枚举常量,让字段需为这个数据类型即可,如下:
1 type attentionLevel int64 2 3 const ( 4 ATTENTION_ONE attentionLevel = 1 5 ATTENTION_TWO attentionLevel = 2 6 ATTENTION_THREE attentionLevel = 3 7 ATTENTION_FOUR attentionLevel = 4 8 ) 9 10 type ArticleType struct { 11 BaseModel BaseModel `gorm:"embedded" json:"base_info"` 12 Name string `gorm:"type:varchar(24);not null;column:name" json:"name"` 13 Count uint `gorm:"type:int(11);not null;column:count" json:"count"` 14 Attention attentionLevel `gorm:"type:smallint(6);not null;column:attention" json:"attention"` 15 Level int `gorm:"type:smallint(6);not null;column:level" json:"level"` 16 }
1.3 外键
说实话,官网的外键和关联真的挺难理解的,导致我在这里卡了很长时间,这里讲下一对多情况,理解了后一对一、多对多也自然就理解了,现分析出一个可用的结果如下(未用reference):
定义外键:
1 type ArticleType struct { 2 BaseModel BaseModel `gorm:"embedded" json:"base_info"` 3 Name string `gorm:"type:varchar(24);not null;column:name" json:"name"` 4 Count uint `gorm:"type:int(11);not null;column:count" json:"count"` 5 Attention attentionLevel `gorm:"type:smallint(6);not null;column:attention" json:"attention"` 6 Level int `gorm:"type:smallint(6);not null;column:level" json:"level"` 7 // 自关联外键, 可以为null,可以通过该id找到关联的ArticleType 8 ParentNameId uint `gorm:"type:int(11);column:parent_name_id" json:"parent_name_id"` 9 // 关联自己的那些ArticleType,通过关联外键ParentNameId查找 10 ChildArticleTypes []ArticleType `gorm:"ForeignKey:ParentNameId" json:"child_article_types"` 11 // 关联自己的那些Article,通过关联外键TypeId查找 12 Articles []Article `gorm:"ForeignKey:TypeId" json:"articles"` 13 } 14 15 type Article struct { 16 BaseModel BaseModel `gorm:"embedded" json:"base_info"` 17 Title string `gorm:"type:varchar(128);not null;column:title" json:"title"` 18 Content string `gorm:"type:LONGTEXT;not null;column:content" json:"content"` 19 BackgroundImage string `gorm:"type:varchar(100);not null;column:background_image" json:"background_image"` 20 LikeAmount uint `gorm:"type:int(11);not null;column:like_amount" json:"like_amount"` 21 CollectAmount uint `gorm:"type:int(11);not null;column:collect_amount" json:"collect_amount"` 22 IsTop bool `gorm:"type:bool;not null;column:is_top" json:"is_top"` 23 Level attentionLevel `gorm:"type:smallint(6);not null;column:level" json:"level"` 24 // 关联ArticleType外键,可通过该字段找到关联的ArticleType 25 TypeId uint `gorm:"type:int(11);column:type_id" json:"type_id"` 26 PageViews uint `gorm:"type:int(11);not null;column:page_views" json:"page_views"` 27 }
理解一下:ArticleType与Article一对多,ArticleType为一类、Article为多类;ArticleType由于有两层type所以自关联。多类中定义外键,关联着自己的一类对象(学过mysql的都知道该外键在一类中需为主键);一类中定义切片,可以通过多类关联自己的外键值找到所有关联自己的多类对象(可以参考mysql的sql语句理解)并赋值给该切片,该切片不会在mysql中创建字段,属于orm封装层范畴,如不需要一次性查找出所有关联自己的多类,可不定义。
较为离谱的是,python的orm到这里外键就定义结束了,但gorm不是的,还需要变态的在建表时(建表见)添加上外键:
1 // 对模型类Article添加外键type_id,绑定tb_article_types表的id字段,定义模型类对应的表名称可通过TableName方法定义,具体可看文档 2 DB.Model(&model.Article{}).AddForeignKey("type_id", "tb_article_types(id)", "CASCADE", "CASCADE") 3 // 对模型类ArticleType添加外键parent_name_id,绑定tb_article_types表的id字段 4 DB.Model(&model.ArticleType{}).AddForeignKey("parent_name_id", "tb_article_types(id)", "CASCADE", "CASCADE")
外键查找数据:
多类找一类没什么好说的,通过外键在一类表中查找即可,下面说下通过一类找多类:
1 var parentTypes []model.ArticleType 2 if err := common.DB.Select([]string{"id", "name"}).Where("parent_name_id is null").Find(&parentTypes).Error; err != nil { 3 response.Error(ctx, "select parentType error") 4 return 5 } 6 for _, parentType := range parentTypes { 7 // 语法: DB.Association("[一类中定义的多类切片名称]").Find(&一类对象.多类切片名称) 8 if err := common.DB.Model(&parentType).Association("ChildArticleTypes").Find(&parentType.ChildArticleTypes).Error; err != nil { 9 response.Error(ctx, "select childTypes error") 10 return 11 } 12 }
二、连接数据库 & 表迁移
2.1 连接数据库
通过gorm.Open即可完成连接数据库操作,会返回DB操作对象,默认已经是数据库连接池了
1 DB, err = gorm.Open("mysql", "[username]:[password]@([host:port])/[database]?charset=[charset]&parseTime=true&loc=Local")
后面加parseTime和loc参数可解决时区不一致导致时间不对的问题。
2.1 表迁移
通过DB.AutoMigrate可以完成模型类到表的迁移操作,表不存在则直接创建表,表存在则进行字段对比并添加字段,表完全一致不进行操作。
1 DB.AutoMigrate(&model.ArticleType{}, &model.Article{})
标签:column,外键,json,null,type,gorm 来源: https://www.cnblogs.com/zzmx0/p/16224593.html