refactor ContextCache
This commit is contained in:
parent
a035a9297c
commit
949e8ec5ea
29
README.md
29
README.md
|
@ -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)
|
||||||
|
|
50
README_CN.md
50
README_CN.md
|
@ -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群 技术帮助和讨论。
|
||||||
|
|
|
@ -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]
|
||||||
|
}
|
|
@ -5,7 +5,6 @@
|
||||||
package xorm
|
package xorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -52,8 +51,7 @@ type Session struct {
|
||||||
lastSQL string
|
lastSQL string
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
84
statement.go
84
statement.go
|
@ -19,47 +19,47 @@ import (
|
||||||
|
|
||||||
// Statement save all the sql info for executing SQL
|
// Statement save all the sql info for executing SQL
|
||||||
type Statement struct {
|
type Statement struct {
|
||||||
RefTable *core.Table
|
RefTable *core.Table
|
||||||
Engine *Engine
|
Engine *Engine
|
||||||
Start int
|
Start int
|
||||||
LimitN int
|
LimitN int
|
||||||
idParam *core.PK
|
idParam *core.PK
|
||||||
OrderStr string
|
OrderStr string
|
||||||
JoinStr string
|
JoinStr string
|
||||||
joinArgs []interface{}
|
joinArgs []interface{}
|
||||||
GroupByStr string
|
GroupByStr string
|
||||||
HavingStr string
|
HavingStr string
|
||||||
ColumnStr string
|
ColumnStr string
|
||||||
selectStr string
|
selectStr string
|
||||||
useAllCols bool
|
useAllCols bool
|
||||||
OmitStr string
|
OmitStr string
|
||||||
AltTableName string
|
AltTableName string
|
||||||
tableName string
|
tableName string
|
||||||
RawSQL string
|
RawSQL string
|
||||||
RawParams []interface{}
|
RawParams []interface{}
|
||||||
UseCascade bool
|
UseCascade bool
|
||||||
UseAutoJoin bool
|
UseAutoJoin bool
|
||||||
StoreEngine string
|
StoreEngine string
|
||||||
Charset string
|
Charset string
|
||||||
UseCache bool
|
UseCache bool
|
||||||
UseAutoTime bool
|
UseAutoTime bool
|
||||||
noAutoCondition bool
|
noAutoCondition bool
|
||||||
IsDistinct bool
|
IsDistinct bool
|
||||||
IsForUpdate bool
|
IsForUpdate bool
|
||||||
TableAlias string
|
TableAlias string
|
||||||
allUseBool bool
|
allUseBool bool
|
||||||
checkVersion bool
|
checkVersion bool
|
||||||
unscoped bool
|
unscoped bool
|
||||||
columnMap columnMap
|
columnMap columnMap
|
||||||
omitColumnMap columnMap
|
omitColumnMap columnMap
|
||||||
mustColumnMap map[string]bool
|
mustColumnMap map[string]bool
|
||||||
nullableMap map[string]bool
|
nullableMap map[string]bool
|
||||||
incrColumns map[string]incrParam
|
incrColumns map[string]incrParam
|
||||||
decrColumns map[string]decrParam
|
decrColumns map[string]decrParam
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue