Get adds context cache feature (#1102)
* context * add context cache feature * remove global context cache * remove global context cache * reset statment * fix bug * remove unused params * refactor ContextCache * refactor ContextCache * update README * update README * disable global cache on context cache test
This commit is contained in:
parent
fa23a9774a
commit
7a9249de33
52
README.md
52
README.md
|
@ -34,6 +34,8 @@ Xorm is a simple and powerful ORM for Go.
|
|||
|
||||
* Postgres schema support
|
||||
|
||||
* Context Cache support
|
||||
|
||||
## Drivers Support
|
||||
|
||||
Drivers for Go's sql package which currently support database/sql includes:
|
||||
|
@ -358,6 +360,56 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern
|
|||
return session.Commit()
|
||||
```
|
||||
|
||||
* Or you can use `Transaction` to replace above codes.
|
||||
|
||||
```Go
|
||||
res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) {
|
||||
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
|
||||
if _, err := session.Insert(&user1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user2 := Userinfo{Username: "yyy"}
|
||||
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
```
|
||||
|
||||
* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
|
||||
|
||||
```Go
|
||||
sess := engine.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
var context = xorm.NewMemoryContextCache()
|
||||
|
||||
var c2 ContextGetStruct
|
||||
has, err := sess.ID(1).ContextCache(context).Get(&c2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c2.Id)
|
||||
assert.EqualValues(t, "1", c2.Name)
|
||||
sql, args := sess.LastSQL()
|
||||
assert.True(t, len(sql) > 0)
|
||||
assert.True(t, len(args) > 0)
|
||||
|
||||
var c3 ContextGetStruct
|
||||
has, err = sess.ID(1).ContextCache(context).Get(&c3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c3.Id)
|
||||
assert.EqualValues(t, "1", c3.Name)
|
||||
sql, args = sess.LastSQL()
|
||||
assert.True(t, len(sql) == 0)
|
||||
assert.True(t, len(args) == 0)
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md). And we also provide [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) to discuss.
|
||||
|
|
52
README_CN.md
52
README_CN.md
|
@ -32,6 +32,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
|
||||
* 内置SQL Builder支持
|
||||
|
||||
* 上下文缓存支持
|
||||
|
||||
## 驱动支持
|
||||
|
||||
目前支持的Go数据库驱动和对应的数据库如下:
|
||||
|
@ -360,6 +362,56 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern
|
|||
return session.Commit()
|
||||
```
|
||||
|
||||
* 事物的简写方法
|
||||
|
||||
```Go
|
||||
res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) {
|
||||
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
|
||||
if _, err := session.Insert(&user1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user2 := Userinfo{Username: "yyy"}
|
||||
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
```
|
||||
|
||||
* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
|
||||
|
||||
```Go
|
||||
sess := engine.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
var context = xorm.NewMemoryContextCache()
|
||||
|
||||
var c2 ContextGetStruct
|
||||
has, err := sess.ID(1).ContextCache(context).Get(&c2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c2.Id)
|
||||
assert.EqualValues(t, "1", c2.Name)
|
||||
sql, args := sess.LastSQL()
|
||||
assert.True(t, len(sql) > 0)
|
||||
assert.True(t, len(args) > 0)
|
||||
|
||||
var c3 ContextGetStruct
|
||||
has, err = sess.ID(1).ContextCache(context).Get(&c3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c3.Id)
|
||||
assert.EqualValues(t, "1", c3.Name)
|
||||
sql, args = sess.LastSQL()
|
||||
assert.True(t, len(sql) == 0)
|
||||
assert.True(t, len(args) == 0)
|
||||
```
|
||||
|
||||
## 贡献
|
||||
|
||||
如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)。您也可以加入QQ群 技术帮助和讨论。
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
// ContextCache is the interface that operates the cache data.
|
||||
type ContextCache interface {
|
||||
// Put puts value into cache with key.
|
||||
Put(key string, val interface{})
|
||||
// Get gets cached value by given key.
|
||||
Get(key string) interface{}
|
||||
}
|
||||
|
||||
type memoryContextCache map[string]interface{}
|
||||
|
||||
// NewMemoryContextCache return memoryContextCache
|
||||
func NewMemoryContextCache() memoryContextCache {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Put puts value into cache with key.
|
||||
func (m memoryContextCache) Put(key string, val interface{}) {
|
||||
m[key] = val
|
||||
}
|
||||
|
||||
// Get gets cached value by given key.
|
||||
func (m memoryContextCache) Get(key string) interface{} {
|
||||
return m[key]
|
||||
}
|
|
@ -102,6 +102,12 @@ func (session *Session) Close() {
|
|||
}
|
||||
}
|
||||
|
||||
// ContextCache enable context cache or not
|
||||
func (session *Session) ContextCache(context ContextCache) *Session {
|
||||
session.statement.context = context
|
||||
return session
|
||||
}
|
||||
|
||||
// IsClosed returns if session is closed
|
||||
func (session *Session) IsClosed() bool {
|
||||
return session.db == nil
|
||||
|
|
|
@ -7,6 +7,7 @@ package xorm
|
|||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
|
@ -66,7 +67,28 @@ func (session *Session) get(bean interface{}) (bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...)
|
||||
context := session.statement.context
|
||||
if context != nil {
|
||||
res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args))
|
||||
if res != nil {
|
||||
structValue := reflect.Indirect(reflect.ValueOf(bean))
|
||||
structValue.Set(reflect.Indirect(reflect.ValueOf(res)))
|
||||
session.lastSQL = ""
|
||||
session.lastSQLArgs = nil
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...)
|
||||
if err != nil || !has {
|
||||
return has, err
|
||||
}
|
||||
|
||||
if context != nil {
|
||||
context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
|
||||
|
|
|
@ -319,3 +319,70 @@ func TestGetStructId(t *testing.T) {
|
|||
assert.True(t, has)
|
||||
assert.EqualValues(t, 2, maxid.Id)
|
||||
}
|
||||
|
||||
func TestContextGet(t *testing.T) {
|
||||
type ContextGetStruct struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
assert.NoError(t, prepareEngine())
|
||||
assertSync(t, new(ContextGetStruct))
|
||||
|
||||
_, err := testEngine.Insert(&ContextGetStruct{Name: "1"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
sess := testEngine.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
context := NewMemoryContextCache()
|
||||
|
||||
var c2 ContextGetStruct
|
||||
has, err := sess.ID(1).NoCache().ContextCache(context).Get(&c2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c2.Id)
|
||||
assert.EqualValues(t, "1", c2.Name)
|
||||
sql, args := sess.LastSQL()
|
||||
assert.True(t, len(sql) > 0)
|
||||
assert.True(t, len(args) > 0)
|
||||
|
||||
var c3 ContextGetStruct
|
||||
has, err = sess.ID(1).NoCache().ContextCache(context).Get(&c3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c3.Id)
|
||||
assert.EqualValues(t, "1", c3.Name)
|
||||
sql, args = sess.LastSQL()
|
||||
assert.True(t, len(sql) == 0)
|
||||
assert.True(t, len(args) == 0)
|
||||
}
|
||||
|
||||
func TestContextGet2(t *testing.T) {
|
||||
type ContextGetStruct2 struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
assert.NoError(t, prepareEngine())
|
||||
assertSync(t, new(ContextGetStruct2))
|
||||
|
||||
_, err := testEngine.Insert(&ContextGetStruct2{Name: "1"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
context := NewMemoryContextCache()
|
||||
|
||||
var c2 ContextGetStruct2
|
||||
has, err := testEngine.ID(1).NoCache().ContextCache(context).Get(&c2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c2.Id)
|
||||
assert.EqualValues(t, "1", c2.Name)
|
||||
|
||||
var c3 ContextGetStruct2
|
||||
has, err = testEngine.ID(1).NoCache().ContextCache(context).Get(&c3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, 1, c3.Id)
|
||||
assert.EqualValues(t, "1", c3.Name)
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ type Statement struct {
|
|||
exprColumns map[string]exprParam
|
||||
cond builder.Cond
|
||||
bufferSize int
|
||||
context ContextCache
|
||||
}
|
||||
|
||||
// Init reset all the statement's fields
|
||||
|
@ -99,6 +100,7 @@ func (statement *Statement) Init() {
|
|||
statement.exprColumns = make(map[string]exprParam)
|
||||
statement.cond = builder.NewCond()
|
||||
statement.bufferSize = 0
|
||||
statement.context = nil
|
||||
}
|
||||
|
||||
// NoAutoCondition if you do not want convert bean's field as query condition, then use this function
|
||||
|
|
Loading…
Reference in New Issue