go-xorm

//Install Library
go get github.com/go-xorm/xorm
//Xorm 命令行工具
go get github.com/go-xorm/cmd/xorm
// 数据库驱动
go get github.com/mattn/go-sqlite3
go get -u github.com/go-sql-driver/mysql

https://github.com/mattn/go-sqlite3
https://github.com/go-sql-driver/mysql

使用 xorm 命令行工具

sqlite3 先要编译一下 xorm

  1. cd to %GOPATH%/src/github.com/go-xorm/cmd/xorm
  2. run “go build -tags sqlite3”
  3. a new “xorm” will be generated at current dir
  4. copy the new “xorm” to %GOPATH%/bin
  5. run your “xorm reverse …” again. Done!

xorm reverse sqlite3 test.db templates/goxorm

名称映射规则

xorm内置了三种IMapper实现:core.SnakeMapper,core.SameMapper和core.GonicMapper
SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;
SameMapper支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名

engine.SetMapper(core.SnakeMapper{})

| name | 当前field对应的字段的名称 |
| — | — | — |
| pk | 是否是Primary Key |
| name | 当前field对应的字段的名称 |
| autoincr | 是否是自增 |
| [not ]null 或 notnull | 是否可以为空 |
| unique | 是否是唯一 |
| index | 是否是索引 |
| extends | 应用于一个匿名成员结构体或者非匿名成员结构体之上
| - | 这个Field将不进行字段映射 |
| -> | Field将只写入到数据库而不从数据库读取 |
| <- | Field将只从数据库读取,而不写入到数据库 |
| created | Field将在Insert时自动赋值为当前时间 |
| updated | Field将在Insert或Update时自动赋值为当前时间 |
|deleted | Field将在Delete时设置为当前时间,并且当前记录不删除 |
| version | Field将会在insert时默认为1,每次更新自动加1 |
| default 0或default(0) | 设置默认值,紧跟的内容如果是Varchar等需要加上单引号 |
| json | 表示内容将先转成Json格式 |

连接数据库

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

var engine *xorm.Engine

func main() {
var err error
engine, err = xorm.NewEngine("sqlite3", "./test.db")
}
import (
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
)

var engine *xorm.Engine

func main() {
var err error
engine, err = xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
}

自动同步表结构

ync2会进行如下这些操作:

自动检测和创建表,这个检测是根据表的名字
自动检测和新增表中的字段,这个检测是根据字段名,同时对表中多余的字段给出警告信息
自动检测,创建和删除索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称。因此这里需要注意,如果在一个有大量数据的表中引入新的索引,数据库可能需要一定的时间来建立索引。
自动转换varchar字段类型到text字段类型,自动警告其它字段类型在模型和数据库之间不一致的情况。
自动警告字段的默认值,是否为空信息在模型和数据库之间不匹配的情况

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

查询

results, err := engine.Query("select * from user")

// 注意这里首字母要大写,导出该字段,类型名和表名对应,字段名和表字段名对应
type T_user struct {
F_id int64 `xorm:"not null pk autoincr INT(11)"`
F_ui_id int64 `xorm:"INT(11)"`
F_group_id int64 `xorm:" INT(11)"`
F_pwd string `xorm:"not null VARCHAR(32)"`
F_name string `xorm:"not null VARCHAR(32)"`
}

// 根据结构体中存在的非空数据来获取单条数据
user := &T_user{F_name: "admin", F_pwd: "4acb4bc224acbbe3c2bfdcaa39a4324e"}
has, err := engine.Get(user)
fmt.Println(has)

// 根据where获取单条数据
user := new(User)
has, err := engine.Table("t_user").Where("f_name=? and f_pwd=?", "admin", "4acb4bc224acbbe3c2bfdcaa39a4324e").Get(user)
fmt.Println(has)

// 根据where获取单条数据
user := new(T_user)
has, err := engine.Where("f_name=? and f_pwd=?", "admin", "4acb4bc224acbbe3c2bfdcaa39a4324e").Get(user)
fmt.Println(has)

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

根据Id来获得单条数据

has, err := x.Id(id).Get(a)

逐条执行查询到的记录

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 方法所接受的参数是数据表中对应的名称,而不是字段名称。

统计记录条数- Count方法

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

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

更新

affected, err := engine.Exec("update user set .... where ...")

// 在获取到记录之后,我们就需要进行一些修改,然后更新到数据库:
a.Balance += deposit
// 对已有记录进行更新
_, err = x.Update(a)

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

删除

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

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

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

if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}

事务及回滚

// 创建 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()

日志记录

一般情况下,使用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 缓存

只需要在获取到 ORM 引擎之后,进行如下操作
这样就算是使用最基本的缓存功能了。该功能还支持只缓存某些表或排除缓存某些表,详情可以参见 文章首部的官方文档。

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

表操作

是否存在

// 通过结构体判断
{
exists, _ := engine.IsTableExist(&tOpLog{})
fmt.Println(exists)
}
// 通过表名字符串判断
{
exists, _ := engine.IsTableExist("t_123123")
fmt.Println(exists)
}

创建表自定义表名

type User struct {
ID int `xorm:"serial primary key 'id'"`
Name string `xorm:"text not null 'name'"`
Type string `xorm:"text not null 'type'"`
}

func (u *User) TableName() string {
return "users"
}

db, err := xorm.NewEngine("postgres", "postgres://db_admin:db_admin@db/sample_db?sslmode=disable")
db.CreateTables(&User{})