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 { - + - + @@ -152,7 +169,7 @@ type User struct { - + @@ -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 - 缓存的实现原理如下图所示: ![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png)
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)字段类型autoincr是否是自增
[not ]null是否可以为空[not ]null 或 notnull是否可以为空
unique或unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引