Go组件学习——gorm四步带你搞定DB增删改查

1、简介

ORM

Object-Relationl Mapping, 它的作用是映射数据库和对象之间的关系,方便我们在实现数据库操作的时候不用去写复杂的sql语句,把对数据库的操作上升到对于对象的操作。

gorm

gorm就是基于Go语言实现的ORM库。

类似于Java生态里大家听到过的Mybatis、Hibernate、SpringData等。

Github

https://github.com/jinzhu/gorm

官方文档

https://gorm.io/

2、如何使用Gorm

只要四步就能上手gorm,可以尽情的沉浸在毫无技术含量的CRUD世界。

2.1 下载gorm库

下载gorm库

go get -u github.com/jinzhu/gorm

这是比较原始的方式,现在有了go mod,我们可以更方便的配置,甚至不用配置。

写好代码,在文件下执行go build,go.mod会自动添加对于gorm的依赖包

github.com/jinzhu/gorm v1.9.10

当然,也可以手动添加这个依赖。

具体参见go-demo项目(https://github.com/DMinerJackie/go-demo

2.2 创建DB连接

建立数据库连接

package main

import (
  "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

func main() {
    var err error
    db, connErr := gorm.Open("mysql", "root:rootroot@/dqm?charset=utf8&parseTime=True&loc=Local")
    if connErr != nil {
        panic("failed to connect database")
    }
    defer db.Close()
  db.SingularTable(true)
}

gorm支持很多数据源包括PostgreSQL、MySQL等。

这里连接的是MySQL,所以需要引用"github.com/jinzhu/gorm/dialects/mysql"驱动。

通过上面声明,已经获取数据库的连接。

db.SingularTable(true)这句的作用后面会提到。

2.3 创建映射表结构的struct

定义数据库表结构对应的struct

比如这里我们要操作的是表test表,表结构如下

CREATE TABLE `test` (
  `id` bigint(20) NOT NULL,
  `name` varchar(5) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

于是我们对应可以定义struct结构如下

type Test struct {
    ID   int64  `gorm:"type:bigint(20);column:id;primary_key"`
    Name string `gorm:"type:varchar(5);column:name"`
    Age  int    `gorm:"type:int(11);column:age"`
}

每个字段后面的gorm是结构标记,可以用于声明对应数据库字段的属性。

比如ID后面的约束为该字段为bigint(20)类型,对应列表为id,且该字段为主键。

除此以外,还有更加丰富的标签定义参见官方文档:http://gorm.io/zh_CN/docs/models.html

2.4 CRUD

有了前面三步的铺垫,下面就可以执行真正写数据库操作了。

比如"增"

test := &Test{
        ID:3,
        Name:"jackie",
        Age:18,
    }
    db.Create(test)

比如"删"

test := &Test{
        ID:3,
        Name:"jackie",
        Age:18,
    }
db.Delete(test)

比如"改"

test := &Test{
        ID:   3,
        Name: "hello",
        Age:  18,
    }
db.Model(&test).Update("name", "world")

比如"查"

var testResult Test
db.Where("name = ?", "hello").First(&testResult)
fmt.Println("result: ", testResult)

如果只是想做个纯粹的CRUDer,掌握上面四步就算是会用gorm了。

如果还想来点花式的,更深入的,继续往下看~~~

3、表名和结构体如何映射

从上面四步,我们只看到在创建DB链接的时候,提供的信息仅仅到数据库,那么gorm是如何做到将表结构和你定义的struct映射起来的呢?

有三种方式可以实现,如果以下三种方式都没有实现,如果你是创建表,则gorm默认会在你定义的struct名后面加上”s“,比如上面就会创建tests表。

3.1 db.SingularTable(true)

通过db.SingularTable(true),gorm会在创建表的时候去掉”s“的后缀

3.2 实现TableName方法

func (Test) TableName() string {
    return "test"
}

TableName方法定义在scope.go的tabler接口中

type tabler interface {
    TableName() string
}

3.3 通过Table API声明

db.Table("test").Where("name = ?", "hello").First(&testResult)

在CRUD前,指明需要操作的表名也是OK的。

4、其他花式操作

下面花式API操作使用表dqm_user_role,对应struct如下

type DqmUserRole struct {
    ID        int64     `gorm:"column:id;primary_key" json:"id"`
    UserId    string    `gorm:"column:user_id" json:"user_id"`
    RoleId    string    `gorm:"column:role_id" json:"role_id"`
    CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
    UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
}

表中初始数据如下

image.png

以下API均亲测可用

First

var dqmUserRole DqmUserRole
// 按照主键顺序的第一条记录
db.First(&dqmUserRole)
fmt.Println("roleId: ", dqmUserRole.RoleId)

Last

var dqmUserRole1 DqmUserRole
// 按照主键顺序的最后一条记录
db.Last(&dqmUserRole1)
fmt.Println("roleId: ", dqmUserRole1.RoleId)

Find

var dqmUserRoels []DqmUserRole
// 所有记录
db.Find(&dqmUserRoels)
fmt.Println("dqmUserRoles: ", dqmUserRoels)

Where

var dqmUserRole3 DqmUserRole
// 根据条件查询得到满足条件的第一条记录
db.Where("role_id = ?", "2").First(&dqmUserRole3)
fmt.Println("where roleId: ", dqmUserRole3.RoleId)

var dqmUserRoles4 []DqmUserRole
// 根据条件查询得到满足条件的所有记录
db.Where("user_id = ?", "1").Find(&dqmUserRoles4)
fmt.Println("where dqmUserRoles: ", dqmUserRoles4)

var dqmUserRole5 []DqmUserRole
// like模糊查询
db.Where("role_id like ?", "%2").Find(&dqmUserRole5)
fmt.Println("where dqmUserRoles: ", dqmUserRole5)

var dqmUserRole6 []DqmUserRole
db.Where("updated_at > ?", "2019-02-08 18:08:27").Find(&dqmUserRole6)
fmt.Println("where dqmUserRoles: ", dqmUserRole6)

var dqmUserRole7 DqmUserRole
// struct结构查询条件
db.Where(&DqmUserRole{RoleId: "1,2", UserId: "1"}).First(&dqmUserRole7)
fmt.Println("where dqmUserRole: ", dqmUserRole7)

var dqmUserRole8 DqmUserRole
// map结构查询条件
db.Where(map[string]interface{}{"role_id": "1,2", "user_id": "1"}).Find(&dqmUserRole8)
fmt.Println("where dqmUserRole: ", dqmUserRole8)

Not

var dqmUserRole9 DqmUserRole
db.Not([]int64{1}).First(&dqmUserRole9)
fmt.Println("not dqmUserRole: ", dqmUserRole9)

Or

var dqmUserRole10 []DqmUserRole
db.Where(&DqmUserRole{RoleId: "1,2"}).Or(map[string]interface{}{"user_id": "2"}).Find(&dqmUserRole10)
fmt.Println("or dqmUserRoles: ", dqmUserRole10)

FirstOrInit和Attrs

var dqmUserRole11 DqmUserRole
// 查不到该条记录,则使用attrs值替换
db.Where("user_id = ?", "0").Attrs("role_id", "12").FirstOrInit(&dqmUserRole11)
fmt.Println("after FirstOrInit: ", dqmUserRole11)

var dqmUserRole12 DqmUserRole
// 查到记录,则使用数据库中的值
db.Where("user_id = ?", "1").Attrs("role_id", "2").FirstOrInit(&dqmUserRole12)
fmt.Println("after FirstOrInit: ", dqmUserRole12)

FirstOrInit和Assign

var dqmUserRole13 DqmUserRole
// 不管是否找到对应记录,使用Assign值替代查询到的值
db.Where("role_id = ?", "1,2").Assign(DqmUserRole{UserId: "15"}).FirstOrInit(&dqmUserRole13)
fmt.Println("assign dqmUserRole: ", dqmUserRole13)

FirstOrCreate

var dqmUserRole14 DqmUserRole
// 如果记录存在则返回结果,如果不存在则创建
db.Where(&DqmUserRole{UserId: "3", RoleId: "3"}).FirstOrCreate(&dqmUserRole14)
fmt.Println("firstOrCreate dqmUserRole: ", dqmUserRole14)

Order

var dqmUserRole16 []DqmUserRole
db.Order("user_id desc").Find(&dqmUserRole16) // 注意这里的order要在find前面,否则不生效
fmt.Println("order dqmUserRoles: ", dqmUserRole16)

Limit和Offset

var dqmUserRole18 []DqmUserRole
db.Limit(10).Offset(2).Find(&dqmUserRole18) // 如果只有offset没有limit则不会生效
fmt.Println("offset dqmUserRoles: ", dqmUserRole18)

Scan

type Result struct {
        Id int64
    }
var results []Result
db.Select("id").Where("user_id in (?)", []string{"1", "2"}).Find(&dqmUserRole20).Scan(&results)
fmt.Println("ids: ", results)

支持执行原生sql

var dqmUserRole24 []DqmUserRole
db.Exec("select * from dqm_user_role").Find(&dqmUserRole24)
fmt.Println("sql dqmUserRole: ", dqmUserRole24)

事务

tx := db.Begin()
defer func() {
    if r := recover(); r != nil {
    tx.Rollback()
}
if err != nil {
    tx.Rollback()
} else {
    tx.Commit()
}
}()
if err = tx.Create(&DqmUserRole{UserId: "8", RoleId: "8"}).Error; err != nil {
  //tx.Rollback()
    //return
}

if err = tx.Create(&DqmUserRole{UserId: "9", RoleId: "9"}).Error; err != nil {
    //tx.Rollback()
    //return
}

错误处理

var dqmUserRole25 DqmUserRole
err = db.Where("role_id = ?", 54321).First(&dqmUserRole25).Error
if err == gorm.ErrRecordNotFound {
  fmt.Println("ErrRecordNotFound, record not found")
} else {
  fmt.Println("err: ", err)
}
fmt.Println("err dqmUserRole: ", dqmUserRole25)

5、总结

gorm作为一款orm库,几乎满足了一个CRUDer的一切想象。实现灵活,花样繁多。

有了gorm,就不需要再在代码中维护sql语句了。

后面有时间会再看看gorm的实现,作为国人开源的第一个orm库,目前star已经超过15k,值得深入学习下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容