Xorm

https://lunny.gitbooks.io/xorm-manual-zh-cn/content/chapter-01/index.html

https://xorm.io/docs/chapter-01/readme/

创建Orm引擎 #

在xorm里面,可以同时存在多个Orm引擎,一个Orm引擎称为Engine,一个Engine一般只对应一个数据库。Engine通过调用xorm.NewEngine生成,如:

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:[email protected]/test?charset=utf8")
}

or

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")//数据库文件路径
}

一般情况下如果只操作一个数据库,只需要创建一个engine即可。engine是GoRutine安全的。

创建完成engine之后,并没有立即连接数据库,此时可以通过engine.Ping()来进行数据库的连接测试是否可以连接到数据库。另外对于某些数据库有连接超时设置的,可以通过起一个定期Ping的Go程来保持连接鲜活。

对于有大量数据并且需要分区的应用,也可以根据规则来创建多个Engine,比如:

var err error
for i:=0;i<5;i++ {
    engines[i], err = xorm.NewEngine("sqlite3", fmt.Sprintf("./test%d.db", i))
}

engine可以通过engine.Close来手动关闭,但是一般情况下可以不用关闭,在程序退出时会自动关闭。

NewEngine传入的参数和sql.Open传入的参数完全相同,因此,在使用某个驱动前,请查看此驱动中关于传入参数的说明文档。以下为各个驱动的连接符对应的文档链接:

在engine创建完成后可以进行一些设置,如:

1.调试,警告以及错误等显示设置,默认如下均为false

  • engine.ShowSQL = true,则会在控制台打印出生成的SQL语句;
  • engine.ShowDebug = true,则会在控制台打印调试信息;
  • engine.ShowError = true,则会在控制台打印错误信息;
  • engine.ShowWarn = true,则会在控制台打印警告信息;

2.如果希望将信息不仅打印到控制台,而是保存为文件,那么可以通过类似如下的代码实现,NewSimpleLogger(w io.Writer)接收一个io.Writer接口来将数据写入到对应的设施中。

f, err := os.Create("sql.log")
    if err != nil {
        println(err.Error())
        return
    }
defer f.Close()
engine.Logger = xorm.NewSimpleLogger(f)

3.engine内部支持连接池接口和对应的函数。

  • 如果需要设置连接池的空闲数大小,可以使用engine.SetMaxIdleConns()来实现。
  • 如果需要设置最大打开连接数,则可以使用engine.SetMaxOpenConns()来实现。
  • 使用engine.SetConnMaxLifetime()设置最大寿命。此方法仅支持Go 1.6+。

定义表结构体 #

xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下:

名称映射规则 #

名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由core.IMapper接口的实现者来管理,xorm内置了三种IMapper实现:core.SnakeMappercore.SameMappercore.GonicMapper。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名。

  • SnakeMapper在每个单词(大写)之间插入一个_(下划线),以获取表名或列名。
  • SameMapper在结构和表之间使用相同的名称。
  • GonicMapper基本上与SnakeMapper相同,但不在常用的首字母缩略词之间插入下划线。例如,ID在GonicMapper中转换为id,但在SnakeMapper中将ID转换为i_d

当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用

engine.SetMapper(core.SameMapper{})

同时需要注意的是:

  • 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
  • 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
engine.SetTableMapper(core.SameMapper{})
engine.SetColumnMapper(core.SnakeMapper{})

当结构自动映射到数据库的表时,下表描述了它们如何相互更改:

go type’s kind value method xorm type
implemented Conversion Conversion.ToDB / Conversion.FromDB Text
int, int8, int16, int32, uint, uint8, uint16, uint32 Int
int64, uint64 BigInt
float32 Float
float64 Double
complex64, complex128 json.Marshal / json.UnMarshal Varchar(64)
[]uint8 Blob
array, slice, map except []uint8 json.Marshal / json.UnMarshal Text
bool 1 or 0 Bool
string Varchar(255)
time.Time DateTime
cascade struct primary key field value BigInt
struct json.Marshal / json.UnMarshal Text
Others

前缀映射,后缀映射和缓存映射 #

  • 通过 core.NewPrefixMapper(core.SnakeMapper{}, "prefix") 可以创建一个在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
  • 通过 core.NewSufffixMapper(core.SnakeMapper{}, "suffix") 可以创建一个在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper换成SameMapper或者你自定义的Mapper。
  • 通过 core.NewCacheMapper(core.SnakeMapper{}) 可以创建一个组合了其它的映射规则,起到在内存中缓存曾经映射过的命名映射。

例如,如果希望所有的表名都在结构体自动命名的基础上加一个前缀而字段名不加前缀,则可以在engine创建完成后执行以下语句:

tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, "prefix")
engine.SetTableMapper(tbMapper)

执行之后,结构体 type User struct 默认对应的表名就变成了 prefix_user 了,而之前默认的是 user

Column属性定义 #

我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如:

type User struct {
    Id   int64
    Name string  `xorm:"varchar(25) notnull unique 'usr_name'"`
}

对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看字段类型对应表。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。

具体的Tag规则如下,另Tag中的关键字均不区分大小写,但字段名根据不同的数据库是区分大小写:

name 当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名,如与其它关键字冲突,请使用单引号括起来。
pk 是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。
当前支持30多种字段类型,详情参见本文最后一个表格 字段类型
autoincr 是否是自增
[not ]null 或 notnull 是否可以为空
unique或unique(uniquename) 是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引
index或index(indexname) 是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引
extends 应用于一个匿名成员结构体或者非匿名成员结构体之上,表示此结构体的所有成员也映射到数据库中,不过extends只加载一级深度
- 这个Field将不进行字段映射
-> 这个Field将只写入到数据库而不从数据库读取
<- 这个Field将只从数据库读取,而不写入到数据库
created 这个Field将在Insert时自动赋值为当前时间
updated 这个Field将在Insert或Update时自动赋值为当前时间
deleted 这个Field将在Delete时设置为当前时间,并且当前记录不删除
version 这个Field将会在insert时默认为1,每次更新自动加1
default 0 设置默认值,紧跟的内容如果是Varchar等需要加上单引号

另外有如下几条自动映射的规则:

  • 1.如果field名称为Id而且类型为int64并且没有定义tag,则会被xorm视为主键,并且拥有自增属性。如果想用Id以外的名字或非int64类型做为主键名,必须在对应的Tag上加上xorm:"pk"来定义主键,加上xorm:"autoincr"作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性。

  • 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义,如:varchar(1024)

  • 3.支持type MyString string等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型。如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。如果是[]byte或者[]uint8,则不做转换二十直接以二进制方式存储。

  • 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换,这个接口的优先级是最高的。

    type Conversion interface {
      FromDB([]byte) error
      ToDB() ([]byte, error)
    }
    
  • 5.如果一个结构体包含一个Conversion的接口类型,那么在获取数据时,必须要预先设置一个实现此接口的struct或者struct的指针。此时可以在此struct中实现BeforeSet(name string, cell xorm.Cell)方法来进行预先给Conversion赋值。例子参见 testConversion

下表为xorm类型和各个数据库类型的对应表:

xorm mysql sqlite3 postgres remark
BIT BIT INTEGER BIT
TINYINT TINYINT INTEGER SMALLINT
SMALLINT SMALLINT INTEGER SMALLINT
MEDIUMINT MEDIUMINT INTEGER INTEGER
INT INT INTEGER INTEGER
INTEGER INTEGER INTEGER INTEGER
BIGINT BIGINT INTEGER BIGINT
CHAR CHAR TEXT CHAR
VARCHAR VARCHAR TEXT VARCHAR
TINYTEXT TINYTEXT TEXT TEXT
TEXT TEXT TEXT TEXT
MEDIUMTEXT MEDIUMTEXT TEXT TEXT
LONGTEXT LONGTEXT TEXT TEXT
BINARY BINARY BLOB BYTEA
VARBINARY VARBINARY BLOB BYTEA
DATE DATE NUMERIC DATE
DATETIME DATETIME NUMERIC TIMESTAMP
TIME TIME NUMERIC TIME
TIMESTAMP TIMESTAMP NUMERIC TIMESTAMP
TIMESTAMPZ TEXT TEXT TIMESTAMP with zone timestamp with zone info
REAL REAL REAL REAL
FLOAT FLOAT REAL REAL
DOUBLE DOUBLE REAL DOUBLE PRECISION
DECIMAL DECIMAL NUMERIC DECIMAL
NUMERIC NUMERIC NUMERIC NUMERIC
TINYBLOB TINYBLOB BLOB BYTEA
BLOB BLOB BLOB BYTEA
MEDIUMBLOB MEDIUMBLOB BLOB BYTEA
LONGBLOB LONGBLOB BLOB BYTEA
BYTEA BLOB BLOB BYTEA
BOOL TINYINT INTEGER BOOLEAN
SERIAL INT INTEGER SERIAL auto increment
BIGSERIAL BIGINT INTEGER BIGSERIAL auto increment

表结构操作 #

xorm提供了一些动态获取和修改表结构的方法,通过这些方法可以动态同步数据库结构,导出数据库结构,导入数据库结构。

如果您只是需要一个工具,可以直接使用go get github.com/go-xorm/cmd/xorm来安装xorm命令行工具。

获取数据库信息 #

  • DBMetas()

xorm支持获取表结构信息,通过调用engine.DBMetas()可以获取到数据库中所有的表,字段,索引的信息。

  • TableInfo()

根据传入的结构体指针及其对应的Tag,提取出模型对应的表结构信息。这里不是数据库当前的表结构信息,而是我们通过struct建模时希望数据库的表的结构信息

表操作 #

  • CreateTables()

创建表使用engine.CreateTables(),参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。Charset()和StoreEngine()当前仅支持Mysql数据库。

  • IsTableEmpty()

判断表是否为空,参数和CreateTables相同

  • IsTableExist()

判断表是否存在

  • DropTables()

删除表使用engine.DropTables(),参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。

创建索引和唯一索引 #

  • CreateIndexes

根据struct中的tag来创建索引

  • CreateUniques

根据struct中的tag来创建唯一索引

同步数据库结构 #

同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前有两个实现:

  • Sync

Sync将进行如下的同步操作:

* 自动检测和创建表,这个检测是根据表的名字
* 自动检测和新增表中的字段,这个检测是根据字段名
* 自动检测和创建索引和唯一索引,这个检测是根据索引的一个或多个字段名,而不根据索引名称

调用方法如下:

err := engine.Sync(new(User), new(Group))
  • Sync2

Sync2对Sync进行了改进,目前推荐使用Sync2。Sync2函数将进行如下的同步操作:

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

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

调用方法和Sync一样:

err := engine.Sync2(new(User), new(Group))

Dump数据库结构和数据 #

如果需要在程序中Dump数据库的结构和数据可以调用

engine.DumpAll(w io.Writer)

engine.DumpAllFile(fpath string)

DumpAll方法接收一个io.Writer接口来保存Dump出的数据库结构和数据的SQL语句,这个方法导出的SQL语句并不能通用。只针对当前engine所对应的数据库支持的SQL。

Import 执行数据库SQL脚本 #

如果你需要将保存在文件或者其它存储设施中的SQL脚本执行,那么可以调用

engine.Import(r io.Reader)

engine.ImportFile(fpath string)

同样,这里需要对应的数据库的SQL语法支持。

注意

//AllCols()不跳过任何字段,即便字段是空值
	return engine.Id(id).AllCols().Update(bean, condiBeans...)

In(colunm string,args …interface{})*Session #

func (engine *Engine)In(colunm string,args ...interface{})*Session{}

创建新的session实例,并将其存储在类型为engine的结构体中。