golang-xorm库快速学习

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: xormxorm是一个Go语言ORM库. 通过它可以使数据库操作非常简便.全部文档点我用法入门:前提:定义本文中用到的struct和基本代码如下1// 银行账户2type Account struct {3 Id int644 Name string `xo...

xorm
xorm是一个Go语言ORM库. 通过它可以使数据库操作非常简便.

全部文档点我

用法入门:
前提:定义本文中用到的struct和基本代码如下

1// 银行账户
2type Account struct {
3    Id      int64
4    Name    string `xorm:"unique"`
5    Balance float64
6    Version int `xorm:"version"` // 乐观锁
7}
8var x *xorm.Engine
  1. 创建orm引擎

注意:若想配合mysql,需要提前加载mysql驱动,通过如此方式

import _ "github.com/go-sql-driver/mysql"

x,err:=xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")

自动同步表结构

1if err = x.Sync2(new(Account)); err != nil {
2        log.Fatalf("Fail to sync database: %v\n", err)
3    }

Sync2会进行如下这些操作:

自动检测和创建表,这个检测是根据表的名字

自动检测和新增表中的字段,这个检测是根据字段名,同时对表中多余的字段给出警告信息

自动检测,创建和删除索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称。因此这里需要注意,如果在一个有大量数据的表中引入新的索引,数据库可能需要一定的时间来建立索引。

自动转换varchar字段类型到text字段类型,自动警告其它字段类型在模型和数据库之间不一致的情况。

自动警告字段的默认值,是否为空信息在模型和数据库之间不匹配的情况

以上这些警告信息需要将engine.ShowWarn 设置为 true 才会显示。

增删改操作

**增加操作:插入一条新的记录,该记录必须是未存在的,否则会返回错误:
**
_, err := x.Insert(&Account{Name: name, Balance: balance})

删除操作:

_, err := x.Delete(&Account{Id: id})

方法 Delete 接受参数后,会自动根据传进去的值进行查找,然后删除。比如此处,我们指定了 Account 的 ID 字段,那么就会删除 ID 字段值与我们所赋值相同的记录;如果您只对 Name 字段赋值,那么 xorm 就会去查找 Name 字段值匹配的记录。如果多个字段同时赋值,则是多个条件同时满足的记录才会被删除。

删除操作针对的对象没有限制,凡是按照条件查找到的,都会被删除(单个与批量删除)。

获取和修改记录:想要修改的记录必须是提前存在的,所以修改前要先查询所要修改的记录

获取记录:

Get方法

查询单条数据使用Get方法,在调用Get方法时需要传入一个对应结构体的指针,同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询。

  1. 根据Id来获得单条数据:
1a:=&Account{}
2has, err := x.Id(id).Get(a)
  1. 根据where获取单条数据
1a := new(Account)
2has, err := x.Where("name=?", "adn").Get(a)
  1. 根据Account结构体中存在的非空数据来获取单条数据
1a := &Account{Id:1}
2has, err := x.Get(a)

返回的结果为两个参数,一个has(bool类型)为该条记录是否存在,第二个参数err为是否有错误。不管err是否为nil,has都有可能为true或者false。

在获取到记录之后,我们就需要进行一些修改,然后更新到数据库:

1a.Balance += deposit
2// 对已有记录进行更新
3_, err = x.Update(a)

注意,Update接受的参数是指针

批量获取信息

err = x.Desc("balance").Find(&as)

在这里,我们还调用了 Desc 方法对记录按照存款数额将账户从大到小排序。

Find方法的第一个参数为slice的指针或Map指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针。

乐观锁

乐观锁是 xorm 提供的一个比较实用的功能,通过在 tag 中指定 version 来开启它。开启之后,每次对记录进行更新的时候,该字段的值就会自动递增 1。如此一来,您就可以判断是否有其它地方同时修改了该记录,如果是,则应当重新操作,否则会出现错误的数据(同时对一个帐号进行取款操作却只扣了一次的数额)。

事务及回滚
废话不多说,直接上示例代码:

 1// 创建 Session 对象
 2sess := x.NewSession()
 3defer sess.Close()
 4// 开启事务
 5if err = sess.Begin(); err != nil {
 6    return err
 7}
 8
 9if _, err = sess.Update(a1); err != nil {
10    // 发生错误时进行回滚
11    sess.Rollback()
12    return err
13} 
14
15// 完成事务
16return sess.Commit()

统计记录条数- Count方法
统计数据使用Count方法,Count方法的参数为struct的指针并且成为查询条件。

1a := new(Account)
2//返回满足id>1的Account的记录条数
3total, err := x.Where("id >?", 1).Count(a)
4//返回Account所有记录条数
5total,err = x.Count(a)

Iterate方法
Iterate方法提供逐条执行查询到的记录的方法,他所能使用的条件和Find方法完全相同

1err := x.Where("id > ?=)", 30).Iterate(new(Account), func(i int, bean interface{})error{
2    user := bean.(*Account)
3    //do somthing use i and user
4})

我们主要来看迭代函数的声明:它接受 2 个参数,第一个是当前记录所对应的索引(该索引和 ID 的值毫无关系,只是查询后结果的索引),第二个参数则是保存了相关类型的空接口,需要自行断言,例如示例中使用 bean.(*Account) 因为我们知道查询的结构是 Account。

查询特定字段
使用 Cols 方法可以指定查询特定字段,当只有结构中的某个字段的值对您有价值时,就可以使用它:

1x.Cols("name").Iterate(new(Account), printFn)
2
3var printFn = func(idx int, bean interface{}) error {
4    //dosomething
5    return nil
6}

此处,所查询出来的结构只有 Name 字段有值,其它字段均为零值。要注意的是,Cols 方法所接受的参数是数据表中对应的名称,而不是字段名称。

排除特定字段
当您希望刻意忽略某个字段的查询结果时,可以使用 Omit 方法:

x.Omit("name").Iterate(new(Account), printFn)
此处,所查询出来的结构只有 Name 字段为零值。要注意的是,Omit 方法所接受的参数是数据表中对应的名称,而不是字段名称。

查询结果偏移
查询结果偏移在分页应用中最为常见,通过 Limit 方法可以达到一样的目的:

1x.Limit(3, 2).Iterate(new(Account), printFn)

该方法最少接受 1 个参数,第一个参数表示取出的最大记录数;如果传入第二个参数,则表示对查询结果进行偏移。因此,此处的查询结果为偏移 2 个后,再最多取出 3 个记录。

日志记录
一般情况下,使用x.ShowSQL = true来开启 xorm 最基本的日志功能,所有 SQL 都会被打印到控制台,但如果您想要将日志保存到文件,则可以在获取到 ORM 引擎之后,进行如下操作:


1f, err := os.Create("sql.log")
2if err != nil {
3    log.Fatalf("Fail to create log file: %v\n", err)
4    return
5}
6x.Logger = xorm.NewSimpleLogger(f)

LRU 缓存
作为唯一支持 LRU 缓存的一款 ORM,如果不知道如何使用这个特性,那将是非常遗憾。不过,想要使用它也并不困难,只需要在获取到 ORM 引擎之后,进行如下操作:

1cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
2x.SetDefaultCacher(cacher)

这样就算是使用最基本的缓存功能了。该功能还支持只缓存某些表或排除缓存某些表,详情可以参见 文章首部的官方文档。

事件钩子
官方一共提供了 6 类 事件钩子,示例中只演示其中 2 种:BeforeInsert 和 AfterInsert。全部内容查看文章首部官方文档

它们的作用分别会在 进行插入记录之前 和 完成插入记录之后 被调用:

func (a *Account) BeforeInsert() {
log.Printf("before insert: %s", a.Name)
}

func (a *Account) AfterInsert() {
log.Printf("after insert: %s", a.Name)
}

下面是一个简单的银行存取款的小例子

  1package main
  2
  3import (
  4    "errors"
  5    "log"
  6
  7    "github.com/go-xorm/xorm"
  8    _ "github.com/mattn/go-sqlite3"
  9)
 10
 11// 银行账户
 12type Account struct {
 13    Id      int64
 14    Name    string `xorm:"unique"`
 15    Balance float64
 16    Version int `xorm:"version"` // 乐观锁
 17}
 18
 19// ORM 引擎
 20var x *xorm.Engine
 21
 22func init() {
 23    // 创建 ORM 引擎与数据库
 24    var err error
 25    x, err = xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")
 26    if err != nil {
 27        log.Fatalf("Fail to create engine: %v\n", err)
 28    }
 29
 30    // 同步结构体与数据表
 31    if err = x.Sync(new(Account)); err != nil {
 32        log.Fatalf("Fail to sync database: %v\n", err)
 33    }
 34}
 35
 36// 创建新的账户
 37func newAccount(name string, balance float64) error {
 38    // 对未存在记录进行插入
 39    _, err := x.Insert(&Account{Name: name, Balance: balance})
 40    return err
 41}
 42
 43// 获取账户信息
 44func getAccount(id int64) (*Account, error) {
 45    a := &Account{}
 46    // 直接操作 ID 的简便方法
 47    has, err := x.Id(id).Get(a)
 48    // 判断操作是否发生错误或对象是否存在
 49    if err != nil {
 50        return nil, err
 51    } else if !has {
 52        return nil, errors.New("Account does not exist")
 53    }
 54    return a, nil
 55}
 56
 57// 用户转账
 58func makeTransfer(id1, id2 int64, balance float64) error {
 59    // 创建 Session 对象
 60    sess := x.NewSession()
 61    defer sess.Close()
 62    // 启动事务
 63    if err = sess.Begin(); err != nil {
 64        return err
 65    }
 66
 67    a1, err := getAccount(id1)
 68    if err != nil {
 69        return err
 70    }
 71
 72    a2, err := getAccount(id2)
 73    if err != nil {
 74        return err
 75    }
 76
 77    if a1.Balance < balance {
 78        return errors.New("Not enough balance")
 79    }
 80
 81    a1.Balance -= balance
 82
 83    a2.Balance += balance
 84
 85    if _, err = sess.Update(a1); err != nil {
 86        // 发生错误时进行回滚
 87        sess.Rollback()
 88        return err
 89    }
 90    if _, err = sess.Update(a2); err != nil {
 91        sess.Rollback()
 92        return err
 93    }
 94    // 完成事务
 95    return sess.Commit()
 96
 97    return nil
 98}
 99
100// 用户存款
101func makeDeposit(id int64, deposit float64) (*Account, error) {
102    a, err := getAccount(id)
103    if err != nil {
104        return nil, err
105    }
106    sess := x.NewSession()
107    defer sess.Close()
108    if err = sess.Begin(); err != nil {
109        return nil, err
110    }
111    a.Balance += deposit
112    // 对已有记录进行更新
113    if _, err = sess.Update(a); err != nil {
114        sess.Rollback()
115        return nil, err
116    }
117
118    return a, sess.Commit()
119}
120
121// 用户取款
122func makeWithdraw(id int64, withdraw float64) (*Account, error) {
123    a, err := getAccount(id)
124    if err != nil {
125        return nil, err
126    }
127    if a.Balance < withdraw {
128        return nil, errors.New("Not enough balance")
129    }
130    sess := x.NewSession()
131    defer sess.Close()
132    if _, err = sess.Begin(); err != nil {
133        return nil, err
134    }
135    a.Balance -= withdraw
136    if _, err = sess.Update(a); err != nil {
137        return nil, err
138    }
139    return a, sess.Commit()
140}
141
142// 按照 ID 正序排序返回所有账户
143func getAccountsAscId() (as []Account, err error) {
144    // 使用 Find 方法批量获取记录
145    err = x.Find(&as)
146    return as, err
147}
148
149// 按照存款倒序排序返回所有账户
150func getAccountsDescBalance() (as []Account, err error) {
151    // 使用 Desc 方法使结果呈倒序排序
152    err = x.Desc("balance").Find(&as)
153    return as, err
154}
155
156// 删除账户
157func deleteAccount(id int64) error {
158    // 通过 Delete 方法删除记录
159    _, err := x.Delete(&Account{Id: id})
160    return err
161}

原文发布时间为:2019-1-4
本文作者: Golang语言社区
本文来自云栖社区合作伙伴“ Golang语言社区”,了解相关信息可以关注“Golangweb”微信公众号

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
16天前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
32 4
|
1月前
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
|
2月前
|
安全 Java Go
【Golang入门】简介与基本语法学习
Golang语言入门教程,介绍了Go语言的简介、基本语法、程序结构、变量和常量、控制结构、函数、并发编程、接口和类型、导入包、作用域以及错误处理等关键概念,为初学者提供了一个全面的学习起点。
31 0
|
3月前
|
Unix Go
Golang语言标准库time之日期和时间相关函数
这篇文章是关于Go语言日期和时间处理的文章,介绍了如何使用Go标准库中的time包来处理日期和时间。
59 3
|
4月前
|
机器学习/深度学习 存储 人工智能
Golang bytes 包学习
Golang bytes 包学习
26 3
|
4月前
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
302 2
|
5月前
|
编译器 Go C语言
通过例子学习在golang中调试程序
【7月更文挑战第4天】Go语言支持使用cgo进行汇编调试,官方文档在golang.org/doc/asm。注意,调试Go运行时可能遇到变量不可用或行号错误,需谨慎使用step命令。
74 1
通过例子学习在golang中调试程序
|
4月前
|
存储 JSON Go
一文搞懂 Golang 高性能日志库 Zerolog
一文搞懂 Golang 高性能日志库 Zerolog
436 0
|
4月前
|
JSON Go 数据格式
[golang]标准库-json
[golang]标准库-json
|
6月前
|
SQL NoSQL Go
技术经验分享:Golang标准库:errors包应用
技术经验分享:Golang标准库:errors包应用
45 0