golang-xorm库快速学习

xorm

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

全部文档点我

用法入门:

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

// 银行账户
type Account struct {
    Id      int64
    Name    string `xorm:"unique"`
    Balance float64
    Version int `xorm:"version"` // 乐观锁
}
var x *xorm.Engine

创建orm引擎

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

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

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

自动同步表结构

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

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自动成为查询的条件和前面的方法条件组合在一起查询。

a. 根据Id来获得单条数据:

a:=&Account{}
has, err := x.Id(id).Get(a)

b. 根据where获取单条数据

a := new(Account)
has, err := x.Where("name=?", "adn").Get(a)

c. 根据Account结构体中存在的非空数据来获取单条数据

a := &Account{Id:1}
has, err := x.Get(a)

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

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

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

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

批量获取信息

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

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

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

乐观锁

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

事务及回滚

废话不多说,直接上示例代码:

// 创建 Session 对象
sess := x.NewSession()
defer sess.Close()
// 开启事务
if err = sess.Begin(); err != nil {
    return err
}

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

// 完成事务
return sess.Commit()

统计记录条数- Count方法

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

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

Iterate方法

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

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

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

查询特定字段

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

x.Cols("name").Iterate(new(Account), printFn)

var printFn = func(idx int, bean interface{}) error {
    //dosomething
    return nil
}

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

排除特定字段

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

x.Omit("name").Iterate(new(Account), printFn)

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

查询结果偏移

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

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

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

日志记录

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

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

LRU 缓存

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

cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.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)
}

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

package main

import (
    "errors"
    "log"

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

// 银行账户
type Account struct {
    Id      int64
    Name    string `xorm:"unique"`
    Balance float64
    Version int `xorm:"version"` // 乐观锁
}

// ORM 引擎
var x *xorm.Engine

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

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

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

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

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

    a1, err := getAccount(id1)
    if err != nil {
        return err
    }

    a2, err := getAccount(id2)
    if err != nil {
        return err
    }

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

    a1.Balance -= balance

    a2.Balance += balance

    if _, err = sess.Update(a1); err != nil {
        // 发生错误时进行回滚
        sess.Rollback()
        return err
    }
    if _, err = sess.Update(a2); err != nil {
        sess.Rollback()
        return err
    }
    // 完成事务
    return sess.Commit()

    return nil
}

// 用户存款
func makeDeposit(id int64, deposit float64) (*Account, error) {
    a, err := getAccount(id)
    if err != nil {
        return nil, err
    }
    sess := x.NewSession()
    defer sess.Close()
    if err = sess.Begin(); err != nil {
        return nil, err
    }
    a.Balance += deposit
    // 对已有记录进行更新
    if _, err = sess.Update(a); err != nil {
        sess.Rollback()
        return nil, err
    }

    return a, sess.Commit()
}

// 用户取款
func makeWithdraw(id int64, withdraw float64) (*Account, error) {
    a, err := getAccount(id)
    if err != nil {
        return nil, err
    }
    if a.Balance < withdraw {
        return nil, errors.New("Not enough balance")
    }
    sess := x.NewSession()
    defer sess.Close()
    if _, err = sess.Begin(); err != nil {
        return nil, err
    }
    a.Balance -= withdraw
    if _, err = sess.Update(a); err != nil {
        return nil, err
    }
    return a, sess.Commit()
}

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

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

// 删除账户
func deleteAccount(id int64) error {
    // 通过 Delete 方法删除记录
    _, err := x.Delete(&Account{Id: id})
    return err
}

注:本文参考

Go名库讲解 (https://github.com/Unknwon/go-rock-libraries-showcases)

官方文档 (http://www.xorm.io/docs/)

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2017-10-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?

继续讨论EF中使用存储过程的问题,这回着重讨论的是为存储过程的参数进行赋值的问题。说得更加具体一点,是如何为实体映射的Delete存储过程参数进行赋值的问题。关...

2209
来自专栏Java3y

Hibernate入门这一篇就够了

前言 本博文主要讲解介绍Hibernate框架,ORM的概念和Hibernate入门,相信你们看了就会使用Hibernate了! 什么是Hibernate框架?...

3254
来自专栏PingCAP的专栏

TiDB 源码阅读系列文章(三)SQL 的一生

上一篇文章讲解了 TiDB 项目的结构以及三个核心部分,本篇文章从 SQL 处理流程出发,介绍哪里是入口,对 SQL 需要做哪些操作,知道一个 SQL 是从哪里...

40715
来自专栏抠抠空间

Flask之基本使用与配置

Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于...

932
来自专栏JetpropelledSnake

SQL学习之SQL注入总结

2803
来自专栏SpringBoot 核心技术

欢迎使用开源持久化框架 MyBatis Enhance

2453
来自专栏程序员的SOD蜜

PDF.NET(PWMIS数据开发框架)之SQL-MAP目标和规范

SQL-MAP的目标: 集中管理SQL语句,所有SQL语句放在专门的配置文件中进行管理; 通过替换SQL配置文件,达到平滑切换数据库到另外一个数据库,比如从O...

2588
来自专栏web编程技术分享

【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第四节)

4315
来自专栏程序猿

带您理解SQLSERVER是如何执行一个查询的

带您理解SQLSERVER是如何执行一个查询的 连接方式和请求 如果你是一个开发者,并且你的程序使用SQLSERVER来做数据库的话 你会想知道当你用你的程序执...

5029
来自专栏数据处理

Delete Duplicate Emails

1774

扫码关注云+社区