* 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 testtags/v0.7.1
@@ -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. | |||
@@ -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 | |||