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