Go语言框架之GORM使用(四) | 青训营

85 阅读3分钟

GORM的使用入门

通过GORM存储自定义的数据

如果你想把结构体,数组等非常规数据类型存放进数据库,并且在取出来的时候让知道GORM该如何接受。我们可以采用自定义数据类型来实现。

如果想使用自定义数据类型,需要实现Scanner和Value接口。Value接口可以告知GORM如何把非常规数据存放进数据库,而Scanner接口则告知GORM怎么去从数据库取出自定义数据类型。

例如,我们可以将结构体转换成json格式存放入数据库当中。

json: Javascript Object Nanotation 是一种数据交换格式,常用于前后端数据传输。前后端可以将需要通信的内容转换成json字符串,另一端再解析字符串成对应的数据结构,如struct结构,从而完成通信。

假设我们定义结构体如下:

type UserInfo struct {
    Name String `json:"name"`
    Age  int    `json:"age"`

通过已经实现了的Value接口,将结构体存放入数据库当中:

func (u UserInfo) Value() (driver.Value, error) {
    return json.Marshal(u)
}

json.Marshal():将数据编码成json字符串。注意:必须要是可导出成员才能被编码,即结构体首字母应大写。如果字段后有json标签,则用标签名作为json的Key,否则直接用字段名作为Key。

再定义Scanner结构,将数据从数据库中读出。

func (u *UserInfo) Scan(value interface{}) error {
  bytes, ok := value.([]byte)
  if !ok {
    return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
  }

  info := UserInfo{}
  err := json.Unmarshal(bytes, &info)
  *u = info
  return err
}

json.Unmarshal:和json.Marshal()相对应,将json字符串解码到对应的数据结构。例如上面例子就将传入的json字符串内容解码到了UserInfo结构体。

注意:json.Unmarshal接收体必须要传入指针,虽然不会报错,但无法对info实例进行赋值。如果有匹配不了的项会自动忽略。

type User struct {
    ID   uint
    Info UserInfo `gorm:"type:string"`
}

对此结构体进行数据插入的时候,gorm自动会将UserInfo结构体转化为纯字符串放入数据库当中。

DB.Create(&User{
  ID: 1,
  Info: UserInfo{
    Name: "xxx",
    Age:    21,
  },
})

事务

事务就是用户定义的一系列数据库操作,这些操作可以视为一个完整的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。

一个很常见的例子便是银行转账,张三给李四转账100元,在程序里面,张三的余额就要-100,李四的余额就要+100,整个事件是一个不可分割的整体。假如说有哪一步执行出错,例如张三的余额没有成功减去100,就会导致银行的总账目对不上。

在gorm中,为了确保数据一致性,会在事务里执行写入操作(创建、更新、删除),一般是默认为开启状态。

禁用gorm默认事务之后,可以得到性能上30%左右的提升。

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  SkipDefaultTransaction: true,
})

gorm可以使用Transaction()方法定义事务,如

DB.Transaction(func(tx *gorm.DB) error {
  zhangsan.Money -= 100
  err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Error
  if err != nil {
    return err
  }
  lisi.Money += 100
  err = tx.Model(&lisi).Update("money", lisi.Money).Error
  if err != nil {
    return err
  }
  return nil
})

这样,加入程序出现错误,就会自动回滚到开始状态,不会出现数据不一致的情况。

Transaction() 也可以进行嵌套。

除了使用 Transaction 方法之外,也可以使用手动方法定义事务。

首先需要使用 tx := database.Begin 开始事务。

tx.Create(...) 在事务中执行一些数据库操作,注意,需要使用之前定义的'tx'而不是'database'。不仅是 Create 方法,常用的数据库方法都可以使用,在事务里面完成增删改查等操作。

操作遇到错误的时候,需要使用 tx.Rollback 回滚事务,保证数据一致性。

如果没有遇到任何错误,便可以将事务进行提交 tx.Commit。

另外,GORM 提供了 SavePoint、Rollbackto 方法,来提供保存点以及回滚至保存点功能