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
* Context Get Cache
* Context Cache support
## Drivers Support
@ -360,14 +360,37 @@ 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.NewContextCache()
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.True(t, has)
assert.EqualValues(t, 1, c2.Id)
@ -377,7 +400,7 @@ return session.Commit()
assert.True(t, len(args) > 0)
var c3 ContextGetStruct
has, err = sess.ID(1).Get(&c3)
has, err = sess.ID(1).ContextCache(context).Get(&c3)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c3.Id)

View File

@ -32,6 +32,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 内置SQL Builder支持
* 上下文缓存支持
## 驱动支持
目前支持的Go数据库驱动和对应的数据库如下
@ -360,6 +362,54 @@ 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 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群 技术帮助和讨论。

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

View File

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

View File

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

@ -19,47 +19,47 @@ import (
// Statement save all the sql info for executing SQL
type Statement struct {
RefTable *core.Table
Engine *Engine
Start int
LimitN int
idParam *core.PK
OrderStr string
JoinStr string
joinArgs []interface{}
GroupByStr string
HavingStr string
ColumnStr string
selectStr string
useAllCols bool
OmitStr string
AltTableName string
tableName string
RawSQL string
RawParams []interface{}
UseCascade bool
UseAutoJoin bool
StoreEngine string
Charset string
UseCache bool
UseAutoTime bool
noAutoCondition bool
IsDistinct bool
IsForUpdate bool
TableAlias string
allUseBool bool
checkVersion bool
unscoped bool
columnMap columnMap
omitColumnMap columnMap
mustColumnMap map[string]bool
nullableMap map[string]bool
incrColumns map[string]incrParam
decrColumns map[string]decrParam
exprColumns map[string]exprParam
cond builder.Cond
bufferSize int
enableContextCache bool
RefTable *core.Table
Engine *Engine
Start int
LimitN int
idParam *core.PK
OrderStr string
JoinStr string
joinArgs []interface{}
GroupByStr string
HavingStr string
ColumnStr string
selectStr string
useAllCols bool
OmitStr string
AltTableName string
tableName string
RawSQL string
RawParams []interface{}
UseCascade bool
UseAutoJoin bool
StoreEngine string
Charset string
UseCache bool
UseAutoTime bool
noAutoCondition bool
IsDistinct bool
IsForUpdate bool
TableAlias string
allUseBool bool
checkVersion bool
unscoped bool
columnMap columnMap
omitColumnMap columnMap
mustColumnMap map[string]bool
nullableMap map[string]bool
incrColumns map[string]incrParam
decrColumns map[string]decrParam
exprColumns map[string]exprParam
cond builder.Cond
bufferSize int
context ContextCache
}
// Init reset all the statement's fields
@ -100,7 +100,7 @@ func (statement *Statement) Init() {
statement.exprColumns = make(map[string]exprParam)
statement.cond = builder.NewCond()
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