refactor ContextCache

This commit is contained in:
Lunny Xiao 2018-09-23 10:18:23 +08:00
parent a035a9297c
commit 949e8ec5ea
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A
7 changed files with 186 additions and 62 deletions

View File

@ -34,7 +34,7 @@ Xorm is a simple and powerful ORM for Go.
* Postgres schema support * Postgres schema support
* Context Get Cache * Context Cache support
## Drivers Support ## Drivers Support
@ -360,14 +360,37 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern
return session.Commit() 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. * Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
```Go ```Go
sess := engine.NewSession() sess := engine.NewSession()
defer sess.Close() defer sess.Close()
var context = xorm.NewContextCache()
var c2 ContextGetStruct var c2 ContextGetStruct
has, err := sess.ID(1).ContextCache().Get(&c2) has, err := sess.ID(1).ContextCache(context).Get(&c2)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has) assert.True(t, has)
assert.EqualValues(t, 1, c2.Id) assert.EqualValues(t, 1, c2.Id)
@ -377,7 +400,7 @@ return session.Commit()
assert.True(t, len(args) > 0) assert.True(t, len(args) > 0)
var c3 ContextGetStruct var c3 ContextGetStruct
has, err = sess.ID(1).Get(&c3) has, err = sess.ID(1).ContextCache(context).Get(&c3)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has) assert.True(t, has)
assert.EqualValues(t, 1, c3.Id) assert.EqualValues(t, 1, c3.Id)

View File

@ -32,6 +32,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 内置SQL Builder支持 * 内置SQL Builder支持
* 上下文缓存支持
## 驱动支持 ## 驱动支持
目前支持的Go数据库驱动和对应的数据库如下 目前支持的Go数据库驱动和对应的数据库如下
@ -360,6 +362,54 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern
return session.Commit() 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 c2 ContextGetStruct
has, err := sess.ID(1).ContextCache().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).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群 技术帮助和讨论。 如果您也想为Xorm贡献您的力量请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)。您也可以加入QQ群 技术帮助和讨论。

27
context_cache.go Normal file
View File

@ -0,0 +1,27 @@
// 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 and expire time.
Put(key string, val interface{})
// Get gets cached value by given key.
Get(key string) interface{}
}
type memoryContextCache map[string]interface{}
func NewMemoryContextCache() memoryContextCache {
return make(map[string]interface{})
}
func (m memoryContextCache) Put(key string, val interface{}) {
m[key] = val
}
func (m memoryContextCache) Get(key string) interface{} {
return m[key]
}

View File

@ -5,7 +5,6 @@
package xorm package xorm
import ( import (
"context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
@ -53,7 +52,6 @@ type Session struct {
lastSQLArgs []interface{} lastSQLArgs []interface{}
err error err error
context context.Context
} }
// Clone copy all the session's content and return a new session // Clone copy all the session's content and return a new session
@ -84,7 +82,6 @@ func (session *Session) Init() {
session.lastSQL = "" session.lastSQL = ""
session.lastSQLArgs = []interface{}{} session.lastSQLArgs = []interface{}{}
session.context = nil
} }
// Close release the connection from pool // Close release the connection from pool
@ -106,8 +103,8 @@ func (session *Session) Close() {
} }
// ContextCache enable context cache or not // ContextCache enable context cache or not
func (session *Session) ContextCache() *Session { func (session *Session) ContextCache(context ContextCache) *Session {
session.statement.enableContextCache = true session.statement.context = context
return session return session
} }

View File

@ -5,7 +5,6 @@
package xorm package xorm
import ( import (
"context"
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
@ -68,9 +67,9 @@ func (session *Session) get(bean interface{}) (bool, error) {
} }
} }
var enableContextCache = session.statement.enableContextCache context := session.statement.context
if session.context != nil { if context != nil {
res := session.context.Value(fmt.Sprintf("%v-%v", sqlStr, args)) res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args))
if res != nil { if res != nil {
structValue := reflect.Indirect(reflect.ValueOf(bean)) structValue := reflect.Indirect(reflect.ValueOf(bean))
structValue.Set(reflect.Indirect(reflect.ValueOf(res))) structValue.Set(reflect.Indirect(reflect.ValueOf(res)))
@ -85,11 +84,8 @@ func (session *Session) get(bean interface{}) (bool, error) {
return has, err return has, err
} }
if enableContextCache { if context != nil {
if session.context == nil { context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean)
session.context = context.Background()
}
session.context = context.WithValue(session.context, fmt.Sprintf("%v-%v", sqlStr, args), bean)
} }
return true, nil return true, nil

View File

@ -335,8 +335,10 @@ func TestContextGet(t *testing.T) {
sess := testEngine.NewSession() sess := testEngine.NewSession()
defer sess.Close() defer sess.Close()
context := NewMemoryContextCache()
var c2 ContextGetStruct var c2 ContextGetStruct
has, err := sess.ID(1).ContextCache().Get(&c2) has, err := sess.ID(1).ContextCache(context).Get(&c2)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has) assert.True(t, has)
assert.EqualValues(t, 1, c2.Id) assert.EqualValues(t, 1, c2.Id)
@ -346,7 +348,7 @@ func TestContextGet(t *testing.T) {
assert.True(t, len(args) > 0) assert.True(t, len(args) > 0)
var c3 ContextGetStruct var c3 ContextGetStruct
has, err = sess.ID(1).Get(&c3) has, err = sess.ID(1).ContextCache(context).Get(&c3)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has) assert.True(t, has)
assert.EqualValues(t, 1, c3.Id) assert.EqualValues(t, 1, c3.Id)
@ -355,3 +357,32 @@ func TestContextGet(t *testing.T) {
assert.True(t, len(sql) == 0) assert.True(t, len(sql) == 0)
assert.True(t, len(args) == 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).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).ContextCache(context).Get(&c3)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c3.Id)
assert.EqualValues(t, "1", c3.Name)
}

View File

@ -59,7 +59,7 @@ type Statement struct {
exprColumns map[string]exprParam exprColumns map[string]exprParam
cond builder.Cond cond builder.Cond
bufferSize int bufferSize int
enableContextCache bool context ContextCache
} }
// Init reset all the statement's fields // Init reset all the statement's fields
@ -100,7 +100,7 @@ func (statement *Statement) Init() {
statement.exprColumns = make(map[string]exprParam) statement.exprColumns = make(map[string]exprParam)
statement.cond = builder.NewCond() statement.cond = builder.NewCond()
statement.bufferSize = 0 statement.bufferSize = 0
statement.enableContextCache = false statement.context = nil
} }
// NoAutoCondition if you do not want convert bean's field as query condition, then use this function // NoAutoCondition if you do not want convert bean's field as query condition, then use this function