其他分享
首页 > 其他分享> > gorm

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