21 KiB
Quick Start
- 1.Create ORM Engine
- 2.Define a struct
- 3. database schema operation
- 4.Insert records
- 5.Query and Count records
- 6.Update records
- 6.1.Optimistic Locking
- 7.Delete records
- 8.Execute SQL command
- 9.Execute SQL query
- 10.Transaction
- 11.Cache
- 12.Xorm Tool
- 13.Examples
- 14.Cases
- 15.FAQ
- 16.Discuss
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/go-xorm/xorm"
)
engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
defer engine.Close()
or
import (
_ "github.com/mattn/go-sqlite3"
"github.com/go-xorm/xorm"
)
engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()
You can create many engines for different databases.Generally, you just need create only one engine. Engine supports run on go routines.
xorm supports four drivers now:
-
MyMysql: github.com/ziutek/mymysql/godrv
-
SQLite: github.com/mattn/go-sqlite3
-
Postgres: github.com/lib/pq
-
MsSql: github.com/lunny/godbc
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 provide DB connection pool settings.
- Use
engine.SetMaxIdleConns()
to set idle connections. - Use
engine.SetMaxOpenConns()
to set Max connections. This methods support only Go 1.2+.
2.Define struct
xorm maps a struct to a database table, the rule is below.
2.1.name mapping rule
use xorm.IMapper interface to implement. There are two IMapper implemented: SnakeMapper
and SameMapper
. SnakeMapper means struct name is word by word and table name or column name as 下划线. SameMapper means same name between struct and table.
SnakeMapper is the default.
engine.SetMapper(SameMapper{})
And you should notice:
- If you want to use other mapping rule, implement IMapper
- Tables's mapping rule could be different from Columns':
engine.SetTableMapper(SameMapper{})
engine.SetColumnMapper(SnakeMapper{})
2.2.Prefix mapping, Suffix Mapping and Cache Mapping
- 通过
engine.NewPrefixMapper(SnakeMapper{}, "prefix")
可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 - 通过
engine.NewSufffixMapper(SnakeMapper{}, "suffix")
可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 - 通过
eneing.NewCacheMapper(SnakeMapper{})
可以起到在内存中缓存曾经映射过的命名映射。
当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
2.3.Tag mapping
如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。
通过engine.Table()
方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用xorm:"'table_name'"
可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
2.4.Column defenition
我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如:
type User struct {
Id int64
Name string `xorm:"varchar(25) not null unique 'usr_name'"`
}
For different DBMS, data types对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看字段类型对应表。
具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写:
name or 'name' | Column Name, optional |
pk | If column is Primary Key |
当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md) | 字段类型 |
autoincr | If autoincrement column |
[not ]null | notnull | if column could be blank |
unique/unique(uniquename) | 是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引 |
index/index(indexname) | 是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引 |
extends | 应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中 |
- | This field will not be mapping |
-> | 这个Field将只写入到数据库而不从数据库读取 |
<- | 这个Field将只从数据库读取,而不写入到数据库 |
created | This field will be filled in current time on insert |
updated | This field will be filled in current time on insert or update |
version | This field will be filled 1 on insert and autoincrement on update |
default 0 | default 'name' | column default value |
另外有如下几条自动映射的规则:
-
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 retrieve database meta info
- DBMetas()
xorm支持获取表结构信息,通过调用
engine.DBMetas()
可以获取到所有的表的信息
3.2.directly table operation
-
CreateTables() 创建表使用
engine.CreateTables()
,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 -
IsTableEmpty() 判断表是否为空,参数和CreateTables相同
-
IsTableExist() 判断表是否存在
-
DropTables() 删除表使用
engine.DropTables()
,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。
3.3.create indexes and uniques
-
CreateIndexes 根据struct中的tag来创建索引
-
CreateUniques 根据struct中的tag来创建唯一索引
3.4.同步数据库结构
同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现:
- 自动检测和创建表,这个检测是根据表的名字 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. Chainable APIs
5.1. Chainable APIs for Queries, Execusions and Aggregations
Queries and Aggregations is basically formed by using Get
, Find
, Count
methods, with conjunction of following chainable APIs to form conditions, grouping and ordering:
查询和统计主要使用Get
, Find
, Count
三个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下:
-
Id([]interface{}) Primary Key lookup
-
Where(string, …interface{}) As SQL conditional WHERE clause
-
And(string, …interface{}) Conditional AND
-
Or(string, …interface{}) Conditional OR
-
Sql(string, …interface{}) 执行指定的Sql语句,并把结果映射到结构体
-
Asc(…string) Ascending ordering on 1 or more fields
-
Desc(…string) Descending ordering on 1 or more fields
-
OrderBy(string) Custom ordering
-
In(string, …interface{}) Conditional IN
-
Cols(…string) Explicity specify query or update columns. e.g.,:
engine.Cols("age", "name").Find(&users)
// SELECT age, name FROM user
engine.Cols("age", "name").Update(&user)
// UPDATE user SET age=? AND name=?
- Omit(...string) Inverse function to Cols, to exclude specify query or update columns. Warning: Don't use with Cols()
engine.Omit("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,则此种方法将无法实现,因此有两种选择:
- 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。
affected, err := engine.Id(id).Cols("age").Update(&user)
- 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
6.1.乐观锁
要使用乐观锁,需要使用version标记
type User struct {
Id int64
Name string
Version int xorm:"version"
}
在Insert时,version标记的字段将会被设置为1,在Update时,Update的内容必须包含version原来的值。
var user User
engine.Id(1).Get(&user)
// SELECT * FROM user WHERE id = ?
engine.Id(1).Update(&user)
// UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ?
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()
defer session.Close()
// 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.缓存
- 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)
- Table's Cache If only some tables need cache, then:
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.MapCacher(&user, cacher)
Caution:
-
When use Cols methods on cache enabled, the system still return all the columns.
-
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:
12.xorm tool
xorm工具提供了xorm命令,能够帮助做很多事情。
12.1.Reverse command
Please visit xorm tool
13.Examples
请访问https://github.com/go-xorm/xorm/tree/master/examples
14.Cases
-
GoDaily,source github.com/govc/godaily
15.FAQ
1.How the xorm tag use both with json?
Use space.
type User struct {
Name string `json:"name" xorm:"name"`
}