Merge branch 'master' of github.com:lunny/xorm

This commit is contained in:
Lunny Xiao 2013-12-31 14:32:36 +08:00
commit 1e9db268f5
2 changed files with 71 additions and 31 deletions

1
benchmark.bat Normal file
View File

@ -0,0 +1 @@
go test -v -bench=. -run=XXX

View File

@ -3,10 +3,11 @@ xorm 快速入门
* [1.创建Orm引擎](#10) * [1.创建Orm引擎](#10)
* [2.定义表结构体](#20) * [2.定义表结构体](#20)
* [2.1.名称映射规则](#21) * [2.1.名称映射规则](#21)
* [2.2.使用Table和Tag改变名称映射](#22) * [2.2.前缀映射规则和后缀映射规则](#22)
* [2.3.Column属性定义](#23) * [2.3.使用Table和Tag改变名称映射](#23)
* [2.4.Go与字段类型对应表](#24) * [2.4.Column属性定义](#24)
* [2.5.Go与字段类型对应表](#25)
* [3.表结构操作](#30) * [3.表结构操作](#30)
* [3.1 获取数据库信息](#31) * [3.1 获取数据库信息](#31)
* [3.2 表操作](#32) * [3.2 表操作](#32)
@ -19,7 +20,8 @@ xorm 快速入门
* [5.3.Get方法](#63) * [5.3.Get方法](#63)
* [5.4.Find方法](#64) * [5.4.Find方法](#64)
* [5.5.Iterate方法](#65) * [5.5.Iterate方法](#65)
* [5.6.Count方法](#66) * [5.6.Count方法](#66)
* [5.7.Rows方法](#67)
* [6.更新数据](#70) * [6.更新数据](#70)
* [6.1.乐观锁](#71) * [6.1.乐观锁](#71)
* [7.删除数据](#80) * [7.删除数据](#80)
@ -69,13 +71,15 @@ xorm当前支持四种驱动如下
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq) * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc)
NewEngine传入的参数和`sql.Open`传入的参数完全相同,因此,使用哪个驱动前,请查看此驱动中关于传入参数的说明文档。 NewEngine传入的参数和`sql.Open`传入的参数完全相同,因此,使用哪个驱动前,请查看此驱动中关于传入参数的说明文档。
在engine创建完成后可以进行一些设置 在engine创建完成后可以进行一些设置
1.设置 1.错误显示设置,默认如下均为`false`
* `engine.ShowSQL = true`则会在控制台打印出生成的SQL语句 * `engine.ShowSQL = true`则会在控制台打印出生成的SQL语句
* `engine.ShowDebug = true`,则会在控制台打印调试信息; * `engine.ShowDebug = true`,则会在控制台打印调试信息;
@ -93,7 +97,7 @@ f, err := os.Create("sql.log")
engine.Logger = f engine.Logger = f
``` ```
3.engine内部支持连接池接口默认使用的Go所实现的连接池同时自带了另外两种实现一种是不使用连接池另一种为一个自实现的连接池。推荐使用Go所实现的连接池。如果要使用自己实现的连接池可以实现`xorm.IConnectPool`并通过`engine.SetPool`进行设置。 3.engine内部支持连接池接口默认使用的Go所实现的连接池同时自带了另外两种实现一种是不使用连接池另一种为一个自实现的连接池。推荐使用Go所实现的连接池。如果要使用自己实现的连接池可以实现`xorm.IConnectPool`并通过`engine.SetPool`进行设置。推荐使用Go默认的连接池。
* 如果需要设置连接池的空闲数大小,可以使用`engine.SetIdleConns()`来实现。 * 如果需要设置连接池的空闲数大小,可以使用`engine.SetIdleConns()`来实现。
* 如果需要设置最大打开连接数,则可以使用`engine.SetMaxConns()`来实现。 * 如果需要设置最大打开连接数,则可以使用`engine.SetMaxConns()`来实现。
@ -111,20 +115,33 @@ xorm支持将一个struct映射为数据库中对应的一张表。映射规则
当前SnakeMapper为默认值如果需要改变时在engine创建完成后使用 当前SnakeMapper为默认值如果需要改变时在engine创建完成后使用
```Go ```Go
engine.Mapper = SameMapper{} engine.SetMapper(SameMapper{})
``` ```
当然如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。 同时需要注意的是:
* 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
```Go
engine.SetTableMapper(SameMapper{})
engine.SetColumnMapper(SnakeMapper{})
```
<a name="22" id="22"></a> <a name="22" id="22"></a>
### 2.2.使用Table和Tag改变名称映射 ### 2.2.前缀映射规则和后缀映射规则
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的后缀。
<a name="23" id="23"></a>
### 2.3.使用Table和Tag改变名称映射
如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。 如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。
通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。 通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。
<a name="23" id="23"></a> <a name="23" id="23"></a>
### 2.3.Column属性定义 ### 2.4.Column属性定义
我们在field对应的Tag中对Column的一些属性进行定义定义的方法基本和我们写SQL定义表结构类似比如 我们在field对应的Tag中对Column的一些属性进行定义定义的方法基本和我们写SQL定义表结构类似比如
``` ```
@ -140,10 +157,10 @@ type User struct {
<table> <table>
<tr> <tr>
<td>name</td><td>当前field对应的字段的名称可选如不写则自动根据field名字和转换规则命名</td> <td>name</td><td>当前field对应的字段的名称可选如不写则自动根据field名字和转换规则命名,如与其它关键字冲突,请使用单引号括起来。</td>
</tr> </tr>
<tr> <tr>
<td>pk</td><td>是否是Primary Key如果在一个struct中有两个字段都使用了此标记则这两个字段构成了复合主键</td> <td>pk</td><td>是否是Primary Key如果在一个struct中有两个字段都使用了此标记则这两个字段构成了复合主键单主键当前支持int32,int,int64,uint32,uint,uint64,string这7中Go的数据类型。</td>
</tr> </tr>
<tr> <tr>
<td>当前支持30多种字段类型详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td> <td>当前支持30多种字段类型详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
@ -152,7 +169,7 @@ type User struct {
<td>autoincr</td><td>是否是自增</td> <td>autoincr</td><td>是否是自增</td>
</tr> </tr>
<tr> <tr>
<td>[not ]null</td><td>是否可以为空</td> <td>[not ]null 或 notnull</td><td>是否可以为空</td>
</tr> </tr>
<tr> <tr>
<td>unique或unique(uniquename)</td><td>是否是唯一如不加括号则该字段不允许重复如加上括号则括号中为联合唯一索引的名字此时如果有另外一个或多个字段和本unique的uniquename相同则这些uniquename相同的字段组成联合唯一索引</td> <td>unique或unique(uniquename)</td><td>是否是唯一如不加括号则该字段不允许重复如加上括号则括号中为联合唯一索引的名字此时如果有另外一个或多个字段和本unique的uniquename相同则这些uniquename相同的字段组成联合唯一索引</td>
@ -188,11 +205,11 @@ type User struct {
另外有如下几条自动映射的规则: 另外有如下几条自动映射的规则:
- 1.如果field名称为`Id`而且类型为`int64`的话会被xorm视为主键并且拥有自增属性。如果想用`Id`以外的名字做为主键名,可以在对应的Tag上加上`xorm:"pk"`来定义主键。 - 1.如果field名称为`Id`而且类型为`int64`的话会被xorm视为主键并且拥有自增属性。如果想用`Id`以外的名字或非int64类型做为主键名必须在对应的Tag上加上`xorm:"pk"`来定义主键,加上`xorm:"autoincr"`作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性
- 2.string类型默认映射为varchar(255)如果需要不同的定义可以在tag中自定义 - 2.string类型默认映射为varchar(255)如果需要不同的定义可以在tag中自定义
- 3.支持`type MyString string`等自定义的field支持Slice, Map等field成员这些成员默认存储为Text类型并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型如果是Blob类型则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。 - 3.支持`type MyString string`等自定义的field支持Slice, Map等field成员这些成员默认存储为Text类型并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型如果是Blob类型则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/lunny/xorm/blob/master/docs/AutoMap.md)
- 4.实现了Conversion接口的类型或者结构体将根据接口的转换方式在类型和数据库记录之间进行相互转换。 - 4.实现了Conversion接口的类型或者结构体将根据接口的转换方式在类型和数据库记录之间进行相互转换。
```Go ```Go
@ -218,7 +235,7 @@ xorm提供了一些动态获取和修改表结构的方法。对于一般的应
## 3.1 获取数据库信息 ## 3.1 获取数据库信息
* DBMetas() * DBMetas()
xorm支持获取表结构信息通过调用`engine.DBMetas()`可以获取到所有的表的信息 xorm支持获取表结构信息通过调用`engine.DBMetas()`可以获取到所有的表,字段,索引的信息
<a name="31" id="31"></a> <a name="31" id="31"></a>
## 3.2.表操作 ## 3.2.表操作
@ -270,7 +287,7 @@ user.Name = "myname"
affected, err := engine.Insert(user) affected, err := engine.Insert(user)
``` ```
在插入成功后如果该结构体有PK字段则PK字段会被自动赋值为数据库中的id 在插入单条数据成功后,如果该结构体有自增字段,则自增字段会被自动赋值为数据库中的id
```Go ```Go
fmt.Println(user.Id) fmt.Println(user.Id)
``` ```
@ -321,22 +338,24 @@ questions[0].Content = "whywhywhwy?"
affected, err := engine.Insert(user, &questions) affected, err := engine.Insert(user, &questions)
``` ```
注意:这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。 这里需要注意以下几点:
* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。
* 多条插入会自动生成`Insert into table values (),(),()`的语句因此这样的语句有一个最大的记录数根据经验测算在150条左右。大于150条后生成的sql语句将太长可能导致执行失败。因此在插入大量数据时目前需要自行分割成每150条插入一次。
<a name="60" id="60"></a> <a name="60" id="60"></a>
## 5.查询和统计数据 ## 5.查询和统计数据
所有的查询条件不区分调用顺序但必须在调用GetFindCount这三个函数之前调用。同时需要注意的一点是在调用的参数中所有的字符字段名均为映射后的数据库的字段名而不是field的名字。 所有的查询条件不区分调用顺序但必须在调用GetFindCount, Iterate, Rows这几个函数之前调用。同时需要注意的一点是,在调用的参数中,如果采用默认的`SnakeMapper`所有的字符字段名均为映射后的数据库的字段名而不是field的名字。
<a name="61" id="61"></a> <a name="61" id="61"></a>
### 5.1.查询条件方法 ### 5.1.查询条件方法
查询和统计主要使用`Get`, `Find`, `Count`个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下: 查询和统计主要使用`Get`, `Find`, `Count`, `Rows`, `Iterate`这几个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下:
* Id(interface{}) * Id(interface{})
传入一个PK字段的值作为查询条件如果是复合主键 传入一个PK字段的值作为查询条件如果是复合主键
`Id(xorm.PK{1, 2})` `Id(xorm.PK{1, 2})`
传入的两个参数按照struct中定义的顺序赋值。 传入的两个参数按照struct中字段出现的顺序赋值。
* Where(string, …interface{}) * Where(string, …interface{})
和Where语句中的条件基本相同作为条件 和Where语句中的条件基本相同作为条件
@ -501,6 +520,22 @@ user := new(User)
total, err := engine.Where("id >?", 1).Count(user) total, err := engine.Where("id >?", 1).Count(user)
``` ```
<a name="67" id="67"></a>
### 5.7.Rows方法
Rows方法和Iterate方法类似提供逐条执行查询到的记录的方法不过Rows更加灵活好用。
```Go
user := new(User)
rows, err := engine.Where("id >?", 1).Rows(user)
if err != nil {
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(user)
//...
}
```
<a name="70" id="70"></a> <a name="70" id="70"></a>
## 6.更新数据 ## 6.更新数据
@ -561,29 +596,33 @@ affected, err := engine.Id(id).Delete(user)
## 8.执行SQL查询 ## 8.执行SQL查询
也可以直接执行一个SQL查询即Select命令。在Postgres中支持原始SQL语句中使用 ` 和 ? 符号。 也可以直接执行一个SQL查询即Select命令。在Postgres中支持原始SQL语句中使用 ` 和 ? 符号。
```Go ```Go
sql := "select * from userinfo" sql := "select * from userinfo"
results, err := engine.Query(sql) results, err := engine.Query(sql)
``` ```
当调用`Query`时,第一个返回值`results`为`[]map[string][]byte`的形式。
<a name="100" id="100"></a> <a name="100" id="100"></a>
## 9.执行SQL命令 ## 9.执行SQL命令
也可以直接执行一个SQL命令即执行Insert Update Delete 等操作。同样在Postgres中支持原始SQL语句中使用 ` 和 ? 符号。 也可以直接执行一个SQL命令即执行Insert Update Delete 等操作。此时不管数据库是何种类型,都可以使用 ` 和 ? 符号。
```Go ```Go
sql = "update userinfo set username=? where id=?" sql = "update `userinfo` set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1) res, err := engine.Exec(sql, "xiaolun", 1)
``` ```
<a name="110" id="110"></a> <a name="110" id="110"></a>
## 10.事务处理 ## 10.事务处理
当使用事务处理时需要创建Session对象。 当使用事务处理时需要创建Session对象。在进行事物处理时可以混用ORM方法和RAW方法如下代码所示
```Go ```Go
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
// add Begin() before any action // add Begin() before any action
err := session.Begin() err := session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1) _, err = session.Insert(&user1)
if err != nil { if err != nil {
@ -638,16 +677,16 @@ engine.MapCacher(&user, nil)
不过需要特别注意不适用缓存或者需要手动编码的地方: 不过需要特别注意不适用缓存或者需要手动编码的地方:
1. 在Get或者Find时使用了Cols方法在开启缓存后此方法无效系统仍旧会取出这个表中的所有字段。 1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存
2. 在使用Exec方法执行了方法之后可能会导致缓存与数据库不一致的地方。因此如果启用缓存尽量避免使用Exec。如果必须使用则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如 2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。
3. 在使用Exec方法执行了方法之后可能会导致缓存与数据库不一致的地方。因此如果启用缓存尽量避免使用Exec。如果必须使用则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如
```Go ```Go
engine.Exec("update user set name = ? where id = ?", "xlw", 1) engine.Exec("update user set name = ? where id = ?", "xlw", 1)
engine.ClearCache(new(User)) engine.ClearCache(new(User))
``` ```
ClearCacheBean
缓存的实现原理如下图所示: 缓存的实现原理如下图所示:
![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) ![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png)