xorm/docs/QuickStartEn.md

20 KiB
Raw Blame History

Quick Start

1.Create ORM Engine

When using xorm, you can create multiple orm engines, an engine means a databse. So you can

import (
	_ "github.com/go-sql-driver/mysql"
	"github.com/lunny/xorm"
)
engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
defer engine.Close()

or

import (
	_ "github.com/mattn/go-sqlite3"
	"github.com/lunny/xorm"
	)
engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()

Generally, you can only create one engine. Engine supports run on go rutines.

xorm supports four drivers now:

NewEngine's parameters are the same as sql.Open. So you should read the drivers' document for parameters' usage.

After engine created, you can do some settings.

1.Logs

  • engine.ShowSQL = true, Show SQL statement on standard output;
  • engine.ShowDebug = true, Show debug infomation on standard output;
  • engine.ShowError = true, Show error infomation on standard output;
  • engine.ShowWarn = true, Show warnning information on standard output;

2.If want to record infomation with another method: use engine.Logger as io.Writer:

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

3.Engine support connection pool. The default pool is database/sql's and also you can use custom pool. Xorm provides two connection pool xorm.NonConnectionPool & xorm.SimpleConnectPool. If you want to use yourself pool, you can use engine.SetPool to set it.

  • Use engine.SetIdleConns() to set idle connections.
  • Use engine.SetMaxConns() to set Max connections. This methods support only Go 1.2+.

2.Define struct

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

2.1.名称映射规则

名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理xorm内置了两种IMapper实现SnakeMapperSameMapper。SnakeMapper支持struct为驼峰式命名表结构为下划线命名之间的转换SameMapper支持相同的命名。

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

engine.Mapper = SameMapper{}

当然如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。

2.2.使用Table和Tag改变名称映射

如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。

通过engine.Table()方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用xorm:"'table_name'"可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。

2.3.Column属性定义

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

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

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

具体的映射规则如下另Tag中的关键字均不区分大小写字段名区分大小写

name当前field对应的字段的名称可选如不写则自动根据field名字和转换规则命名
pk是否是Primary Key当前仅支持int64类型
当前支持30多种字段类型详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)字段类型
autoincr是否是自增
[not ]null是否可以为空
unique或unique(uniquename)是否是唯一如不加括号则该字段不允许重复如加上括号则括号中为联合唯一索引的名字此时如果有另外一个或多个字段和本unique的uniquename相同则这些uniquename相同的字段组成联合唯一索引
index或index(indexname)是否是索引如不加括号则该字段自身为索引如加上括号则括号中为联合索引的名字此时如果有另外一个或多个字段和本index的indexname相同则这些indexname相同的字段组成联合索引
extends应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中
-这个Field将不进行字段映射
->这个Field将只写入到数据库而不从数据库读取
<-这个Field将只从数据库读取而不写入到数据库
created这个Field将在Insert时自动赋值为当前时间
updated这个Field将在Insert或Update时自动赋值为当前时间
default 0设置默认值紧跟的内容如果是Varchar等需要加上单引号

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

  • 1.如果field名称为Id而且类型为int64的话会被xorm视为主键并且拥有自增属性。如果想用Id以外的名字做为主键名可以在对应的Tag上加上xorm:"pk"来定义主键。

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

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

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

type Conversion interface {
	FromDB([]byte) error
	ToDB() ([]byte, error)
}

3.表结构操作

xorm提供了一些动态获取和修改表结构的方法。对于一般的应用很少动态修改表结构则只需调用Sync()同步下表结构即可。

3.1 获取数据库信息

  • DBMetas() xorm支持获取表结构信息通过调用engine.DBMetas()可以获取到所有的表的信息

3.2.表操作

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

  • IsTableEmpty() 判断表是否为空参数和CreateTables相同

  • IsTableExist() 判断表是否存在

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

3.3.创建索引和唯一索引

  • CreateIndexes 根据struct中的tag来创建索引

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

3.4.同步数据库结构

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

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

调用方法如下:

err := engine.Sync(new(User))

4.插入数据

Inserting records use Insert method.

  • Insert one record
user := new(User)
user.Name = "myname"
affected, err := engine.Insert(user)

After inseted, user.Id will be filled with primary key column value.

fmt.Println(user.Id)
  • Insert multiple records by Slice on one table
users := make([]User, 0)
users[0].Name = "name0"
...
affected, err := engine.Insert(&users)
  • Insert multiple records by Slice of pointer on one table
users := make([]*User, 0)
users[0] = new(User)
users[0].Name = "name0"
...
affected, err := engine.Insert(&users)
  • Insert one record on two table.
user := new(User)
user.Name = "myname"
question := new(Question)
question.Content = "whywhywhwy?"
affected, err := engine.Insert(user, question)
  • Insert multiple records on multiple tables.
users := make([]User, 0)
users[0].Name = "name0"
...
questions := make([]Question, 0)
questions[0].Content = "whywhywhwy?"
affected, err := engine.Insert(&users, &questions)
  • Insert one or multple records on multiple tables.
user := new(User)
user.Name = "myname"
...
questions := make([]Question, 0)
questions[0].Content = "whywhywhwy?"
affected, err := engine.Insert(user, &questions)

Notice: If you want to use transaction on inserting, you should use session.Begin() before calling Insert.

5.Query and count

所有的查询条件不区分调用顺序但必须在调用GetFindCount这三个函数之前调用。同时需要注意的一点是在调用的参数中所有的字符字段名均为映射后的数据库的字段名而不是field的名字。

5.1.查询条件方法

查询和统计主要使用Get, Find, Count三个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下:

  • Id(int64) 传入一个PK字段的值作为查询条件

  • Where(string, …interface{}) 和Where语句中的条件基本相同作为条件

  • And(string, …interface{}) 和Where函数中的条件基本相同作为条件

  • Or(string, …interface{}) 和Where函数中的条件基本相同作为条件

  • Sql(string, …interface{}) 执行指定的Sql语句并把结果映射到结构体

  • Asc(…string) 指定字段名正序排序

  • Desc(…string) 指定字段名逆序排序

  • OrderBy(string) 按照指定的顺序进行排序

  • In(string, …interface{}) 某字段在一些值中

  • Cols(…string) 只查询或更新某些指定的字段默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如

engine.Cols("age", "name").Find(&users)
// SELECT age, name FROM user
engine.Cols("age", "name").Update(&user)
// UPDATE user SET age=? AND name=?

其中的参数"age", "name"也可以写成"age, name",两种写法均可

  • Omit(...string) 和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
engine.Cols("age").Update(&user)
// UPDATE user SET name = ? AND department = ?
  • Distinct(…string) 按照参数中指定的字段归类结果
engine.Distinct("age", "department").Find(&users)
// SELECT DISTINCT age, department FROM user

注意当开启了缓存时此方法的调用将在当前查询中禁用缓存。因为缓存系统当前依赖Id而此时无法获得Id

  • Table(nameOrStructPtr interface{}) 传入表名称或者结构体指针如果传入的是结构体指针则按照IMapper的规则提取出表名

  • Limit(int, …int) 限制获取的数目,第一个参数为条数,第二个参数为可选,表示开始位置

  • Top(int) 相当于Limit(int, 0)

  • Join(string,string,string) 第一个参数为连接类型当前支持INNER, LEFT OUTER, CROSS中的一个值第二个参数为表名第三个参数为连接条件

  • GroupBy(string) Groupby的参数字符串

  • Having(string) Having的参数字符串

5.2.临时开关方法

  • NoAutoTime() 如果此方法执行则此次生成的语句中Created和Updated字段将不自动赋值为当前时间

  • NoCache() 如果此方法执行,则此次生成的语句则在非缓存模式下执行

  • UseBool(...string) 当从一个struct来生成查询条件或更新字段时xorm会判断struct的field是否为0,"",nil如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法如果默认不传参数则所有的bool字段都将会被使用如果参数不为空则参数中指定的为字段名则这些字段对应的bool值将被使用。

  • Cascade(bool) 是否自动关联查询field中的数据如果struct的field也是一个struct并且映射为某个Id则可以在查询时自动调用Get方法查询出对应的数据。

5.3.Get one record

Fetch a single object by user

var user = User{Id:27}
has, err := engine.Get(&user)
// or has, err := engine.Id(27).Get(&user)

var user = User{Name:"xlw"}
has, err := engine.Get(&user)

5.4.Find

Fetch multipe objects into a slice or a map, use Find

var everyone []Userinfo
err := engine.Find(&everyone)

users := make(map[int64]Userinfo)
err := engine.Find(&users)
  • also you can use Where, Limit
var allusers []Userinfo
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
  • or you can use a struct query
var tenusers []Userinfo
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0
  • or In function
var tenusers []Userinfo
err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5)
  • The default will query all columns of a table. Use Cols function if you want to select some columns
var tenusers []Userinfo
err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name

5.5.Iterate records

Iterate, like find, but handle records one by one

err := engine.Where("age > ? or name=?)", 30, "xlw").Iterate(new(Userinfo), func(i int, bean interface{})error{
	user := bean.(*Userinfo)
	//do somthing use i and user
})

5.6.Count方法

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

user := new(User)
total, err := engine.Where("id >?", 1).Count(user)

6.更新数据

更新数据使用Update方法Update方法的第一个参数为需要更新的内容可以为一个结构体指针或者一个Map[string]interface{}类型。当传入的为结构体指针时只有非空和0的field才会被作为更新的字段。当传入的为Map类型时key为数据库Column的名字value为要更新的内容。

user := new(User)
user.Name = "myname"
affected, err := engine.Id(id).Update(user)

这里需要注意Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容因此如果需要更新一个值为0则此种方法将无法实现因此有两种选择

  1. 通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。
affected, err := engine.Id(id).Cols("age").Update(&user)
  1. 通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})

7.Delete one or more records

Delete one or more records

  • delete by id
err := engine.Id(1).Delete(&User{})
  • delete by other conditions
err := engine.Delete(&User{Name:"xlw"})

8.Execute SQL query

Of course, SQL execution is also provided.

If select then use Query

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

9.Execute SQL command

If insert, update or delete then use Exec

sql = "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1) 

10.Transaction

session := engine.NewSession()
// add Begin() before any action
err := session.Begin()	
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
	session.Rollback()
	return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
	session.Rollback()
	return
}

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

// add Commit() after all actions
err = session.Commit()
if err != nil {
	return
}

11.缓存

  1. Global Cache Xorm implements cache support. Defaultly, it's disabled. If enable it, use below code.
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.SetDefaultCacher(cacher)

If disable some tables' cache, then:

engine.MapCacher(&user, nil)
  1. Table's Cache If only some tables need cache, then:
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.MapCacher(&user, cacher)

Caution:

  1. When use Cols methods on cache enabled, the system still return all the columns.

  2. When using Exec method, you should clear cache

engine.Exec("update user set name = ? where id = ?", "xlw", 1)
engine.ClearCache(new(User))

Cache implement theory below:

cache design

12.xorm tool

xorm工具提供了xorm命令能够帮助做很多事情。

12.1.Reverse command

Please visit xorm tool

13.Examples

请访问https://github.com/lunny/xorm/tree/master/examples

14.Cases

15.FAQ

1.How the xorm tag use both with json?

Use space.

type User struct {
    Name string `json:"name" xorm:"name"`
}