打印

golang-xorm库快速学习

[复制链接]
187|0
跳转到指定楼层
楼主
本帖最后由 keer_zu 于 2019-6-11 16:44 编辑

xormxorm是一个Go语言ORM库. 通过它可以使数据库操作非常简便.
全部文档
用法入门:前提:定义本文中用到的struct和基本代码如下
  1. // 银行账户
  2. type Account struct {
  3.     Id      int64
  4.     Name    string `xorm:"unique"`
  5.     Balance float64
  6.     Version int `xorm:"version"` // 乐观锁
  7. }
  8. var x *xorm.Engine
复制代码


  • 创建orm引擎
注意:若想配合mysql,需要提前加载mysql驱动,通过如此方式
import _ "github.com/go-sql-driver/mysql"
x,err:=xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")
  2. 自动同步表结构
  1. if err = x.Sync2(new(Account)); err != nil {
  2.         log.Fatalf("Fail to sync database: %v\n", err)
  3.     }
复制代码


  Sync2会进行如下这些操作:
  • 自动检测和创建表,这个检测是根据表的名字
  • 自动检测和新增表中的字段,这个检测是根据字段名,同时对表中多余的字段给出警告信息
  • 自动检测,创建和删除索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称。因此这里需要注意,如果在一个有大量数据的表中引入新的索引,数据库可能需要一定的时间来建立索引。
  • 自动转换varchar字段类型到text字段类型,自动警告其它字段类型在模型和数据库之间不一致的情况。
  • 自动警告字段的默认值,是否为空信息在模型和数据库之间不匹配的情况
以上这些警告信息需要将engine.ShowWarn 设置为 true 才会显示。

  3. 增删改操作
**增加操作:插入一条新的记录,该记录必须是未存在的,否则会返回错误:
**
_, 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自动成为查询的条件和前面的方法条件组合在一起查询。
a. 根据Id来获得单条数据:
  1. a:=&Account{}
  2. has, err := x.Id(id).Get(a)
复制代码



b. 根据where获取单条数据
  1. a := new(Account)
  2. has, err := x.Where("name=?", "adn").Get(a)
复制代码



c. 根据Account结构体中存在的非空数据来获取单条数据
  1. a := &Account{Id:1}
  2. has, err := x.Get(a)
复制代码



返回的结果为两个参数,一个has(bool类型)为该条记录是否存在,第二个参数err为是否有错误。不管err是否为nil,has都有可能为true或者false。
在获取到记录之后,我们就需要进行一些修改,然后更新到数据库:
  1. <pre class="hljs cpp" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; font-size: 13px; padding: 15px; margin-bottom: 20px; line-height: 1.42857; overflow-wrap: normal; color: rgb(171, 178, 191); background: rgb(40, 44, 52); border: 1px solid rgb(204, 204, 204); border-radius: 4px; word-break: break-word !important;"><code class="cpp" style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: inherit; background-color: transparent; border-radius: 0px; border: none; vertical-align: middle;">a.Balance += deposit
  2. <span class="hljs-comment" style="box-sizing: border-box; color: rgb(146, 146, 146);">// 对已有记录进行更新</span>
  3. _, err = x.Update(a)</code></pre>
复制代码


注意,Update接受的参数是指针
批量获取信息
err = x.Desc("balance").Find(&as)
在这里,我们还调用了 Desc 方法对记录按照存款数额将账户从大到小排序。
Find方法的第一个参数为slice的指针或Map指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针。
  4. 乐观锁
乐观锁是 xorm 提供的一个比较实用的功能,通过在 tag 中指定 version 来开启它。开启之后,每次对记录进行更新的时候,该字段的值就会自动递增 1。如此一来,您就可以判断是否有其它地方同时修改了该记录,如果是,则应当重新操作,否则会出现错误的数据(同时对一个帐号进行取款操作却只扣了一次的数额)。
事务及回滚

废话不多说,直接上示例代码:
  1. // 创建 Session 对象
  2. sess := x.NewSession()
  3. defer sess.Close()
  4. // 开启事务
  5. if err = sess.Begin(); err != nil {
  6.     return err
  7. }

  8. if _, err = sess.Update(a1); err != nil {
  9.     // 发生错误时进行回滚
  10.     sess.Rollback()
  11.     return err
  12. }

  13. // 完成事务
  14. return sess.Commit()
复制代码



统计记录条数- Count方法
统计数据使用Count方法,Count方法的参数为struct的指针并且成为查询条件。
  1. a := new(Account)
  2. //返回满足id>1的Account的记录条数
  3. total, err := x.Where("id >?", 1).Count(a)
  4. //返回Account所有记录条数
  5. total,err = x.Count(a)
复制代码


Iterate方法
Iterate方法提供逐条执行查询到的记录的方法,他所能使用的条件和Find方法完全相同
  1. err := 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 方法可以指定查询特定字段,当只有结构中的某个字段的值对您有价值时,就可以使用它:
  1. x.Cols("name").Iterate(new(Account), printFn)

  2. var printFn = func(idx int, bean interface{}) error {
  3.     //dosomething
  4.     return nil
  5. }
复制代码


此处,所查询出来的结构只有 Name 字段有值,其它字段均为零值。要注意的是,Cols 方法所接受的参数是数据表中对应的名称,而不是字段名称。
排除特定字段
当您希望刻意忽略某个字段的查询结果时,可以使用 Omit 方法:
  1. x.Omit("name").Iterate(new(Account), printFn)
复制代码

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

查询结果偏移
查询结果偏移在分页应用中最为常见,通过 Limit 方法可以达到一样的目的:
  1. x.Limit(3, 2).Iterate(new(Account), printFn)
复制代码


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

日志记录
一般情况下,使用x.ShowSQL = true来开启 xorm 最基本的日志功能,所有 SQL 都会被打印到控制台,但如果您想要将日志保存到文件,则可以在获取到 ORM 引擎之后,进行如下操作:
  1. f, err := os.Create("sql.log")
  2. if err != nil {
  3.     log.Fatalf("Fail to create log file: %v\n", err)
  4.     return
  5. }
  6. x.Logger = xorm.NewSimpleLogger(f)
复制代码



LRU 缓存
作为唯一支持 LRU 缓存的一款 ORM,如果不知道如何使用这个特性,那将是非常遗憾。不过,想要使用它也并不困难,只需要在获取到 ORM 引擎之后,进行如下操作:
  1. cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
  2. x.SetDefaultCacher(cacher)
复制代码


这样就算是使用最基本的缓存功能了。该功能还支持只缓存某些表或排除缓存某些表,详情可以参见 文章首部的官方文档。
事件钩子
官方一共提供了 6 类 事件钩子,示例中只演示其中 2 种:BeforeInsert 和 AfterInsert。全部内容查看文章首部官方文档
它们的作用分别会在 进行插入记录之前 和 完成插入记录之后 被调用:
  1. func (a *Account) BeforeInsert() {
  2. log.Printf("before insert: %s", a.Name)
  3. }
  4. func (a *Account) AfterInsert() {
  5. log.Printf("after insert: %s", a.Name)
  6. }
复制代码

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

  2. import (
  3.     "errors"
  4.     "log"

  5.     "github.com/go-xorm/xorm"
  6.     _ "github.com/mattn/go-sqlite3"
  7. )

  8. // 银行账户
  9. type Account struct {
  10.     Id      int64
  11.     Name    string `xorm:"unique"`
  12.     Balance float64
  13.     Version int `xorm:"version"` // 乐观锁
  14. }

  15. // ORM 引擎
  16. var x *xorm.Engine

  17. func init() {
  18.     // 创建 ORM 引擎与数据库
  19.     var err error
  20.     x, err = xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")
  21.     if err != nil {
  22.         log.Fatalf("Fail to create engine: %v\n", err)
  23.     }

  24.     // 同步结构体与数据表
  25.     if err = x.Sync(new(Account)); err != nil {
  26.         log.Fatalf("Fail to sync database: %v\n", err)
  27.     }
  28. }

  29. // 创建新的账户
  30. func newAccount(name string, balance float64) error {
  31.     // 对未存在记录进行插入
  32.     _, err := x.Insert(&Account{Name: name, Balance: balance})
  33.     return err
  34. }

  35. // 获取账户信息
  36. func getAccount(id int64) (*Account, error) {
  37.     a := &Account{}
  38.     // 直接操作 ID 的简便方法
  39.     has, err := x.Id(id).Get(a)
  40.     // 判断操作是否发生错误或对象是否存在
  41.     if err != nil {
  42.         return nil, err
  43.     } else if !has {
  44.         return nil, errors.New("Account does not exist")
  45.     }
  46.     return a, nil
  47. }

  48. // 用户转账
  49. func makeTransfer(id1, id2 int64, balance float64) error {
  50.     // 创建 Session 对象
  51.     sess := x.NewSession()
  52.     defer sess.Close()
  53.     // 启动事务
  54.     if err = sess.Begin(); err != nil {
  55.         return err
  56.     }

  57.     a1, err := getAccount(id1)
  58.     if err != nil {
  59.         return err
  60.     }

  61.     a2, err := getAccount(id2)
  62.     if err != nil {
  63.         return err
  64.     }

  65.     if a1.Balance < balance {
  66.         return errors.New("Not enough balance")
  67.     }

  68.     a1.Balance -= balance

  69.     a2.Balance += balance

  70.     if _, err = sess.Update(a1); err != nil {
  71.         // 发生错误时进行回滚
  72.         sess.Rollback()
  73.         return err
  74.     }
  75.     if _, err = sess.Update(a2); err != nil {
  76.         sess.Rollback()
  77.         return err
  78.     }
  79.     // 完成事务
  80.     return sess.Commit()

  81.     return nil
  82. }

  83. // 用户存款
  84. func makeDeposit(id int64, deposit float64) (*Account, error) {
  85.     a, err := getAccount(id)
  86.     if err != nil {
  87.         return nil, err
  88.     }
  89.     sess := x.NewSession()
  90.     defer sess.Close()
  91.     if err = sess.Begin(); err != nil {
  92.         return nil, err
  93.     }
  94.     a.Balance += deposit
  95.     // 对已有记录进行更新
  96.     if _, err = sess.Update(a); err != nil {
  97.         sess.Rollback()
  98.         return nil, err
  99.     }

  100.     return a, sess.Commit()
  101. }

  102. // 用户取款
  103. func makeWithdraw(id int64, withdraw float64) (*Account, error) {
  104.     a, err := getAccount(id)
  105.     if err != nil {
  106.         return nil, err
  107.     }
  108.     if a.Balance < withdraw {
  109.         return nil, errors.New("Not enough balance")
  110.     }
  111.     sess := x.NewSession()
  112.     defer sess.Close()
  113.     if _, err = sess.Begin(); err != nil {
  114.         return nil, err
  115.     }
  116.     a.Balance -= withdraw
  117.     if _, err = sess.Update(a); err != nil {
  118.         return nil, err
  119.     }
  120.     return a, sess.Commit()
  121. }

  122. // 按照 ID 正序排序返回所有账户
  123. func getAccountsAscId() (as []Account, err error) {
  124.     // 使用 Find 方法批量获取记录
  125.     err = x.Find(&as)
  126.     return as, err
  127. }

  128. // 按照存款倒序排序返回所有账户
  129. func getAccountsDescBalance() (as []Account, err error) {
  130.     // 使用 Desc 方法使结果呈倒序排序
  131.     err = x.Desc("balance").Find(&as)
  132.     return as, err
  133. }

  134. // 删除账户
  135. func deleteAccount(id int64) error {
  136.     // 通过 Delete 方法删除记录
  137.     _, err := x.Delete(&Account{Id: id})
  138.     return err
  139. }
复制代码



注:本文参考







扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

关闭

热门推荐上一条 /5 下一条

在线客服 快速回复 返回顶部 返回列表
汇丰彩票官网 合乐彩票 贵州快3 福建11选5开奖 北京赛车 牛牛彩票 彩尊彩票 重庆彩票 全中彩票 新华彩票