v0.2 Added Cache supported; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes;

This commit is contained in:
Lunny Xiao 2013-09-29 16:43:10 +08:00
parent 82cd602b26
commit 9e38ddfa99
9 changed files with 2544 additions and 84 deletions

View File

@ -25,6 +25,7 @@ Drivers for Go's sql package which currently support database/sql includes:
## Changelog ## Changelog
* **v0.2.0** : Added Cache supported; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes;
* **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping). * **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping).
* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping). * **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping).
* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit. * **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit.
@ -186,6 +187,11 @@ err := engine.Delete(&User{Id:1})
total, err := engine.Count(&User{Name:"xlw"}) total, err := engine.Count(&User{Name:"xlw"})
``` ```
9.Cache
```Go
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.SetDefaultCacher(cacher)
```
## Execute SQL ## Execute SQL
@ -327,7 +333,7 @@ Another is use field tag, field tag support the below keywords which split with
<td>pk</td><td>the field is a primary key</td> <td>pk</td><td>the field is a primary key</td>
</tr> </tr>
<tr> <tr>
<td>more than 30 column type supported, please see [Column Type](https://github.com/lunny/xorm/blob/master/COLUMNTYPE.md)</td><td>column type</td> <td>more than 30 column type supported, please see [Column Type](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>column type</td>
</tr> </tr>
<tr> <tr>
<td>autoincr</td><td>auto incrment</td> <td>autoincr</td><td>auto incrment</td>

View File

@ -24,8 +24,9 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 更新日志 ## 更新日志
* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数SQL语句打印支持io.Writer接口默认打印到控制台新增更多的字段类型支持详见 [映射规则](https://github.com/lunny/xorm/blob/master/QuickStart.md#21)删除废弃的MakeSession和Create函数。 * **v0.2.0** : 新增 缓存支持; 新增数据库表和Struct同名的映射方式 新增Sync同步表结构
* **v0.1.8** : 新增联合index联合unique支持请查看 [映射规则](https://github.com/lunny/xorm/blob/master/QuickStart.md#21)。 * **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数SQL语句打印支持io.Writer接口默认打印到控制台新增更多的字段类型支持详见 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)删除废弃的MakeSession和Create函数。
* **v0.1.8** : 新增联合index联合unique支持请查看 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)。
* **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现可以选择不使用连接池使用系统连接池和使用自带连接池三种实现默认为SysConnectPool即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法在系统退出时应调用此方法。 * **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现可以选择不使用连接池使用系统连接池和使用自带连接池三种实现默认为SysConnectPool即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法在系统退出时应调用此方法。
* **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持 * **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持
* **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代 * **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代
@ -37,7 +38,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 特性 ## 特性
* 支持Struct和数据库表之间的映射映射方式支持命名约定和Tag两种方式映射支持继承 * 支持Struct和数据库表之间的灵活映射,并支持自动同步
* 事务支持 * 事务支持
@ -49,7 +50,9 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 支持数据库连接池 * 支持数据库连接池
* 支持级联加载struct * 支持级联加载Struct
* 支持缓存
## 安装 ## 安装
@ -64,7 +67,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 案例 ## 案例
* [Godaily.org](http://godaily.org) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/)
## FAQ ## FAQ

146
cache.go
View File

@ -29,6 +29,7 @@ func (s *MemoryStore) Put(key, value interface{}) error {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
s.store[key] = value s.store[key] = value
//fmt.Println(s.store)
return nil return nil
} }
@ -57,13 +58,23 @@ type Cacher interface {
DelIds(tableName, sql string) DelIds(tableName, sql string)
DelBean(tableName string, id int64) DelBean(tableName string, id int64)
ClearIds(tableName string) ClearIds(tableName string)
ClearBeans(tableName string)
}
type idNode struct {
tbName string
id int64
}
func newNode(tbName string, id int64) *idNode {
return &idNode{tbName, id}
} }
// LRUCacher implements Cacher according to LRU algorithm // LRUCacher implements Cacher according to LRU algorithm
type LRUCacher struct { type LRUCacher struct {
idList *list.List idList *list.List
sqlList *list.List sqlList *list.List
idIndex map[interface{}]*list.Element idIndex map[string]map[interface{}]*list.Element
sqlIndex map[string]map[interface{}]*list.Element sqlIndex map[string]map[interface{}]*list.Element
store CacheStore store CacheStore
Max int Max int
@ -72,19 +83,19 @@ type LRUCacher struct {
func NewLRUCacher(store CacheStore, max int) *LRUCacher { func NewLRUCacher(store CacheStore, max int) *LRUCacher {
cacher := &LRUCacher{store: store, idList: list.New(), cacher := &LRUCacher{store: store, idList: list.New(),
sqlList: list.New(), idIndex: make(map[interface{}]*list.Element), sqlList: list.New(), Max: max}
Max: max}
cacher.sqlIndex = make(map[string]map[interface{}]*list.Element) cacher.sqlIndex = make(map[string]map[interface{}]*list.Element)
cacher.idIndex = make(map[string]map[interface{}]*list.Element)
return cacher return cacher
} }
func (m *LRUCacher) GetIds(tableName, sql string) interface{} { func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
if v, err := m.store.Get(sql); err == nil {
if _, ok := m.sqlIndex[tableName]; !ok { if _, ok := m.sqlIndex[tableName]; !ok {
m.sqlIndex[tableName] = make(map[interface{}]*list.Element) m.sqlIndex[tableName] = make(map[interface{}]*list.Element)
} }
if v, err := m.store.Get(sql); err == nil {
if el, ok := m.sqlIndex[tableName][sql]; !ok { if el, ok := m.sqlIndex[tableName][sql]; !ok {
el = m.sqlList.PushBack(sql) el = m.sqlList.PushBack(sql)
m.sqlIndex[tableName][sql] = el m.sqlIndex[tableName][sql] = el
@ -92,40 +103,75 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
m.sqlList.MoveToBack(el) m.sqlList.MoveToBack(el)
} }
return v return v
} } else {
if tel, ok := m.sqlIndex[tableName]; ok { if el, ok := m.sqlIndex[tableName][sql]; ok {
if el, ok := tel[sql]; ok {
delete(m.sqlIndex[tableName], sql) delete(m.sqlIndex[tableName], sql)
m.sqlList.Remove(el) m.sqlList.Remove(el)
} }
} }
return nil return nil
} }
func (m *LRUCacher) GetBean(tableName string, id int64) interface{} { func (m *LRUCacher) GetBean(tableName string, id int64) interface{} {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
tid := genId(tableName, id) if _, ok := m.idIndex[tableName]; !ok {
if v, err := m.store.Get(tid); err == nil { m.idIndex[tableName] = make(map[interface{}]*list.Element)
if el, ok := m.idIndex[tid]; ok { }
if v, err := m.store.Get(genId(tableName, id)); err == nil {
if el, ok := m.idIndex[tableName][id]; ok {
m.idList.MoveToBack(el) m.idList.MoveToBack(el)
} else { } else {
el = m.idList.PushBack(tid) el = m.idList.PushBack(newNode(tableName, id))
m.idIndex[tid] = el m.idIndex[tableName][id] = el
} }
return v return v
} } else {
if el, ok := m.idIndex[tid]; ok { // store bean is not exist, then remove memory's index
delete(m.idIndex, tid) if _, ok := m.idIndex[tableName][id]; ok {
m.idList.Remove(el) m.delBean(tableName, id)
if ms, ok := m.sqlIndex[tableName]; ok { m.clearIds(tableName)
for _, v := range ms {
m.sqlList.Remove(v)
}
m.sqlIndex[tableName] = make(map[interface{}]*list.Element)
}
} }
return nil return nil
}
}
func (m *LRUCacher) clearIds(tableName string) {
//fmt.Println("clear ids")
if tis, ok := m.sqlIndex[tableName]; ok {
for sql, v := range tis {
m.sqlList.Remove(v)
m.store.Del(sql)
}
}
m.sqlIndex[tableName] = make(map[interface{}]*list.Element)
}
func (m *LRUCacher) ClearIds(tableName string) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.clearIds(tableName)
}
func (m *LRUCacher) clearBeans(tableName string) {
//fmt.Println("clear beans")
if tis, ok := m.idIndex[tableName]; ok {
//fmt.Println("before clear", len(m.idIndex[tableName]))
for id, v := range tis {
m.idList.Remove(v)
tid := genId(tableName, id.(int64))
m.store.Del(tid)
}
//fmt.Println("after clear", len(m.idIndex[tableName]))
}
m.idIndex[tableName] = make(map[interface{}]*list.Element)
}
func (m *LRUCacher) ClearBeans(tableName string) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.clearBeans(tableName)
} }
func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
@ -139,6 +185,11 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
m.sqlIndex[tableName][sql] = el m.sqlIndex[tableName][sql] = el
} }
m.store.Put(sql, ids) m.store.Put(sql, ids)
/*if m.sqlList.Len() > m.Max {
e := m.sqlList.Front()
node := e.Value.(*idNode)
m.delBean(node.tbName, node.id)
}*/
} }
func (m *LRUCacher) PutBean(tableName string, id int64, obj interface{}) { func (m *LRUCacher) PutBean(tableName string, id int64, obj interface{}) {
@ -146,18 +197,17 @@ func (m *LRUCacher) PutBean(tableName string, id int64, obj interface{}) {
defer m.mutex.Unlock() defer m.mutex.Unlock()
var el *list.Element var el *list.Element
var ok bool var ok bool
tid := genId(tableName, id)
if el, ok = m.idIndex[tid]; !ok { if el, ok = m.idIndex[tableName][id]; !ok {
el = m.idList.PushBack(tid) el = m.idList.PushBack(newNode(tableName, id))
m.idIndex[tid] = el m.idIndex[tableName][id] = el
} }
m.store.Put(tid, obj) m.store.Put(genId(tableName, id), obj)
if m.idList.Len() > m.Max { if m.idList.Len() > m.Max {
e := m.idList.Front() e := m.idList.Front()
m.store.Del(e.Value) node := e.Value.(*idNode)
delete(m.idIndex, e.Value) m.delBean(node.tbName, node.id)
m.idList.Remove(e)
} }
} }
@ -173,34 +223,20 @@ func (m *LRUCacher) DelIds(tableName, sql string) {
} }
} }
func (m *LRUCacher) delBean(tableName string, id int64) {
tid := genId(tableName, id)
if el, ok := m.idIndex[tableName][tid]; ok {
delete(m.idIndex[tableName], tid)
m.idList.Remove(el)
m.clearIds(tableName)
}
m.store.Del(tid)
}
func (m *LRUCacher) DelBean(tableName string, id int64) { func (m *LRUCacher) DelBean(tableName string, id int64) {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
tid := genId(tableName, id) m.delBean(tableName, id)
if el, ok := m.idIndex[tid]; ok {
m.store.Del(tid)
delete(m.idIndex, tid)
m.idList.Remove(el)
if tis, ok := m.sqlIndex[tableName]; ok {
for sql, v := range tis {
m.sqlList.Remove(v)
m.store.Del(sql)
}
m.sqlIndex[tableName] = make(map[interface{}]*list.Element)
}
}
}
func (m *LRUCacher) ClearIds(tableName string) {
m.mutex.Lock()
defer m.mutex.Unlock()
if tis, ok := m.sqlIndex[tableName]; ok {
for sql, v := range tis {
m.sqlList.Remove(v)
m.store.Del(sql)
}
m.sqlIndex[tableName] = make(map[interface{}]*list.Element)
}
} }
func encodeIds(ids []int64) (s string) { func encodeIds(ids []int64) (s string) {

View File

@ -7,6 +7,7 @@ xorm 快速入门
* [2.2.使用Table和Tag改变名称映射](#22) * [2.2.使用Table和Tag改变名称映射](#22)
* [2.3.Column属性定义](#23) * [2.3.Column属性定义](#23)
* [3.创建表](#30) * [3.创建表](#30)
* [3.1.同步数据库结构](#31)
* [4.删除表](#40) * [4.删除表](#40)
* [5.插入数据](#50) * [5.插入数据](#50)
* [6.查询和统计数据](#60) * [6.查询和统计数据](#60)
@ -20,9 +21,11 @@ xorm 快速入门
* [9.执行SQL查询](#90) * [9.执行SQL查询](#90)
* [10.执行SQL命令](#100) * [10.执行SQL命令](#100)
* [11.事务处理](#110) * [11.事务处理](#110)
* [12.Examples](#120) * [12.缓存](#120)
* [13.案例](#130) * [13.Examples](#130)
* [14.讨论](#140) * [14.案例](#140)
* [15.FAQ](#150)
* [15.讨论](#160)
<a name="10" id="10"></a> <a name="10" id="10"></a>
## 1.创建Orm引擎 ## 1.创建Orm引擎
@ -79,6 +82,8 @@ engine.Logger = f
2.engine内部支持连接池接口默认使用的Go所实现的连接池同时自带了另外两种实现一种是不使用连接池另一种为一个自实现的连接池。推荐使用Go所实现的连接池。如果要使用自己实现的连接池可以实现`xorm.IConnectPool`并通过`engine.SetPool`进行设置。 2.engine内部支持连接池接口默认使用的Go所实现的连接池同时自带了另外两种实现一种是不使用连接池另一种为一个自实现的连接池。推荐使用Go所实现的连接池。如果要使用自己实现的连接池可以实现`xorm.IConnectPool`并通过`engine.SetPool`进行设置。
如果需要设置连接池的空闲数大小,可以使用`engine.Pool.SetIdleConns()`来实现。 如果需要设置连接池的空闲数大小,可以使用`engine.Pool.SetIdleConns()`来实现。
3.设置`engine.ShowDebug = true`,则会在控制台打印调试信息。
<a name="20" id="20"></a> <a name="20" id="20"></a>
## 2.定义表结构体 ## 2.定义表结构体
@ -113,7 +118,7 @@ type User struct {
} }
``` ```
对于不同的数据库系统数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义基本的原则是尽量兼容各种数据库的字段类型具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/COLUMNTYPE.md)。 对于不同的数据库系统数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义基本的原则是尽量兼容各种数据库的字段类型具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。
具体的映射规则如下另Tag中的关键字均不区分大小写字段名区分大小写 具体的映射规则如下另Tag中的关键字均不区分大小写字段名区分大小写
@ -125,7 +130,7 @@ type User struct {
<td>pk</td><td>是否是Primary Key当前仅支持int64类型</td> <td>pk</td><td>是否是Primary Key当前仅支持int64类型</td>
</tr> </tr>
<tr> <tr>
<td>当前支持30多种字段类型详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/COLUMNTYPE.md)</td><td>字段类型</td> <td>当前支持30多种字段类型详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
</tr> </tr>
<tr> <tr>
<td>autoincr</td><td>是否是自增</td> <td>autoincr</td><td>是否是自增</td>
@ -181,13 +186,26 @@ type Conversion interface {
<a name="30" id="30"></a> <a name="30" id="30"></a>
## 3.创建表 ## 3.创建表
创建表使用engine.CreateTables()参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine()如果对应的数据库支持这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 创建表使用`engine.CreateTables()`参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine()如果对应的数据库支持这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。
在创建表时会判断表是否已经创建,如果已经创建则不再创建。
在创建表时会判断表是否已经创建如果已经创建则不再创建。在创建表的过程中如果在tag中定义了索引则索引也会自动创建。
<a name="30" id="30"></a>
## 3.1.同步数据库结构
同步表能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现:
1) 自动检测和创建表
2自动检测和新增表中的字段
3自动检测和创建索引和唯一索引
调用方法如下:
```Go
err := engine.Sync(new(User))
```
<a name="40" id="40"></a> <a name="40" id="40"></a>
## 4.删除表 ## 4.删除表
删除表使用engine.DropTables()参数为一个或多个空的对应Struct的指针或者表的名字。 删除表使用`engine.DropTables()`参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入则只删除对应的表如果传入的为Struct则删除表的同时还会删除对应的索引。
<a name="50" id="50"></a> <a name="50" id="50"></a>
## 5.插入数据 ## 5.插入数据
@ -223,8 +241,16 @@ fmt.Println(user.Id)
和Where语句中的条件基本相同作为条件 和Where语句中的条件基本相同作为条件
* Cols(…string) * Cols(…string)
只查询某些指定的字段,默认是查询所有映射的字段 只查询或更新某些指定的字段默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如
```Go
engine.Cols("age, name").Update(&user)
```
or
```Go
engine.Cols("age", "name").Update(&user)
```
* Sql(string, …interface{}) * Sql(string, …interface{})
执行指定的Sql语句并把结果映射到结构体 执行指定的Sql语句并把结果映射到结构体
@ -269,26 +295,54 @@ Having的参数字符串
查询单条数据使用`Get`方法在调用Get方法时需要传入一个对应结构体的指针同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询。 查询单条数据使用`Get`方法在调用Get方法时需要传入一个对应结构体的指针同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询。
如:
1) 根据Id来获得单条数据:
```Go ```Go
user := new(User) user := new(User)
has, err := engine.Id(id).Get(user) has, err := engine.Id(id).Get(user)
``` ```
2) 根据Where来获得单条数据
```Go
user := new(User)
has, err := engine.Where("name=?", "xlw").Get(user)
```
3) 根据user结构体中已有的非空数据来获得单条数据
```Go
user := &User{Id:1}
has, err := engine.Get(user)
```
或者其它条件
返回的结果为两个参数一个为该条记录是否存在第二个参数为是否有错误。如果不管err是否为nilhas都有可能为true或者false。 ```Go
user := &User{Name:"xlw"}
has, err := engine.Get(user)
```
返回的结果为两个参数,一个`has`为该条记录是否存在,第二个参数`err`为是否有错误。不管err是否为nilhas都有可能为true或者false。
<a name="63" id="63"></a> <a name="63" id="63"></a>
### 6.3.Find方法 ### 6.3.Find方法
查询多条数据使用`Find`方法Find方法的第一个参数为slice的指针或Map指针即为查询后返回的结果第二个参数可选为查询的条件struct的指针。 查询多条数据使用`Find`方法Find方法的第一个参数为`slice`的指针或`Map`指针即为查询后返回的结果第二个参数可选为查询的条件struct的指针。
1) 传入Slice用于返回数据
```Go ```Go
var everyone []Userinfo var everyone []Userinfo
err := engine.Find(&everyone) err := engine.Find(&everyone)
```
2) 传入Map用户返回数据map必须为`map[int64]Userinfo`的形式map的key为id
```Go
users := make(map[int64]Userinfo) users := make(map[int64]Userinfo)
err := engine.Find(&users) err := engine.Find(&users)
``` ```
3) 也可以加入条件
```Go
users := make([]Userinfo, 0)
err := engine.Where("age > ? or name=?)", 30, "xlw").Limit(20, 10).Find(&users)
```
<a name="64" id="64"></a> <a name="64" id="64"></a>
### 6.4.Count方法 ### 6.4.Count方法
@ -298,6 +352,7 @@ user := new(User)
total, err := engine.Where("id >?", 1).Count(user) total, err := engine.Where("id >?", 1).Count(user)
``` ```
<a name="65" id-"65"></a>
### 6.5.匿名结构体成员 ### 6.5.匿名结构体成员
如果在struct中拥有一个struct并且在Tag中标记为extends那么该结构体的成员将作为本结构体的成员进行映射。 如果在struct中拥有一个struct并且在Tag中标记为extends那么该结构体的成员将作为本结构体的成员进行映射。
@ -378,6 +433,45 @@ if err != nil {
``` ```
<a name="120" id="120"></a> <a name="120" id="120"></a>
## 12.缓存
xorm内置了一致性缓存支持不过默认并没有开启。要开启缓存需要在engine创建完后进行配置
启用一个全局的内存缓存
```Go
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.SetDefaultCacher(cacher)
```
上述代码采用了LRU算法的一个缓存缓存方式是存放到内存中缓存struct的记录数为1000条缓存针对的范围是所有的表。
如果只想针对部分表,则:
```Go
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
engine.MapCacher(&user, cacher)
```
如果要禁用某个表的缓存,则:
```Go
engine.MapCacher(&user, nil)
```
设置完之后,其它代码基本上就不需要改动了,缓存系统已经在后台运行。
当前实现了内存存储的CacheStore接口MemoryStore如果需要采用其它设备存储可以实现CacheStore接口。
不过需要特别注意不适用缓存或者需要手动编码的地方:
1. 在Get或者Find时使用了Cols方法在开启缓存后此方法无效系统仍旧会取出这个表中的所有字段。
2. 在使用Exec方法执行了方法之后可能会导致缓存与数据库不一致的地方。因此如果启用缓存尽量避免使用Exec。如果必须使用则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如
```Go
engine.Exec("update user set name = ? where id = ?", "xlw", 1)
engine.ClearCache(new(User))
```
缓存的实现原理如下图所示:
![cache design](https://github.com/lunny/xorm/tree/master/docs/cache_design.png)
## 12.Examples ## 12.Examples
请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples) 请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples)
@ -385,7 +479,9 @@ if err != nil {
<a name="130" id="130"></a> <a name="130" id="130"></a>
## 13.案例 ## 13.案例
请访问网站[godaily](http://godaily.org) 和对应的源代码[github.com/govc/godaily](http://github.com/govc/godaily) * [GoDaily Go语言学习网站](http://godaily.org),源代码 [github.com/govc/godaily](http://github.com/govc/godaily)
* [godaily](http://godaily.org) 和对应的源代码[github.com/govc/godaily](http://github.com/govc/godaily)
<a name="140" id="140"></a> <a name="140" id="140"></a>
## 14.讨论 ## 14.讨论

2295
docs/cache_design.graffle Normal file

File diff suppressed because it is too large Load Diff

BIN
docs/cache_design.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

View File

@ -484,7 +484,7 @@ func (engine *Engine) IsEmptyTable(bean interface{}) (bool, error) {
return !has, err return !has, err
} }
func (engine *Engine) isTableExist(bean interface{}) (bool, error) { func (engine *Engine) IsTableExist(bean interface{}) (bool, error) {
t := Type(bean) t := Type(bean)
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
return false, errors.New("bean should be a struct or struct's point") return false, errors.New("bean should be a struct or struct's point")
@ -496,11 +496,32 @@ func (engine *Engine) isTableExist(bean interface{}) (bool, error) {
return has, err return has, err
} }
func (engine *Engine) ClearCache(beans ...interface{}) { func (engine *Engine) ClearCacheBean(bean interface{}, id int64) error {
for _, bean := range beans { t := Type(bean)
table := engine.AutoMap(bean) if t.Kind() != reflect.Struct {
table.Cacher.ClearIds(table.Name) return errors.New("error params")
} }
table := engine.AutoMap(bean)
if table.Cacher != nil {
table.Cacher.ClearIds(table.Name)
table.Cacher.DelBean(table.Name, id)
}
return nil
}
func (engine *Engine) ClearCache(beans ...interface{}) error {
for _, bean := range beans {
t := Type(bean)
if t.Kind() != reflect.Struct {
return errors.New("error params")
}
table := engine.AutoMap(bean)
if table.Cacher != nil {
table.Cacher.ClearIds(table.Name)
table.Cacher.ClearBeans(table.Name)
}
}
return nil
} }
// sync the new struct to database, this method will auto add column, index, unique // sync the new struct to database, this method will auto add column, index, unique

View File

@ -22,8 +22,8 @@ type SyncLoginInfo struct {
// timestamp should be updated by database, so only allow get from db // timestamp should be updated by database, so only allow get from db
TimeStamp string TimeStamp string
// assume // assume
Nonuse int Nonuse int `xorm:"unique"`
Newa string Newa string `xorm:"index"`
} }
func sync(engine *xorm.Engine) error { func sync(engine *xorm.Engine) error {