Skip to content

Commit

Permalink
Get adds context cache feature
Browse files Browse the repository at this point in the history
* 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

* disable global cache on context cache test
  • Loading branch information
xormplus committed Sep 26, 2018
1 parent 95c6519 commit aa450dd
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 4 deletions.
51 changes: 49 additions & 2 deletions README.md
@@ -1,5 +1,5 @@
# xorm
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。请加入原版xorm QQ群:280360085 进行讨论。本定制版API设计相关建议可联系本人QQ:50892683
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。

## 说明

Expand Down Expand Up @@ -27,6 +27,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 支持记录版本(即乐观锁)
* 支持查询结果集导出csv、tsv、xml、json、xlsx、yaml、html功能
* 支持SQL Builder [github.com/go-xorm/builder](https://github.com/go-xorm/builder)
* 上下文缓存支持

## 驱动支持

Expand Down Expand Up @@ -475,6 +476,50 @@ id := engine.SqlTemplateClient(key, &paramMap).Query().Results[0]["id"] //返回

* 本xorm版本同时支持简单事务模型和嵌套事务模型进行事务处理,当使用简单事务模型进行事务处理时,需要创建Session对象,另外当使用Sql()、SqlMapClient()、SqlTemplateClient()方法进行操作时也推荐手工创建Session对象方式管理Session。在进行事物处理时,可以混用ORM方法和RAW方法。注意如果您使用的是mysql,数据库引擎为innodb事务才有效,myisam引擎是不支持事务的。示例代码如下:

* 事物的简写方法
```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)
```

* 简单事务模型的一般用法
```go
session := engine.NewSession()
defer session.Close()
Expand Down Expand Up @@ -506,6 +551,8 @@ if err != nil {
}
```

* 嵌套事务模型

在实际业务开发过程中我们会发现依然还有一些特殊场景,我们需要借助嵌套事务来进行事务处理。本xorm版本也提供了嵌套事务的支持。当使用嵌套事务模型进行事务处理时,同样也需要创建Session对象,与使用简单事务模型进行事务处理不同在于,使用session.Begin()创建简单事务时,直接在同一个session下操作,而使用嵌套事务模型进行事务处理时候,使用session.BeginTrans()创建嵌套事务时,将返回Transaction实例,后续操作则在同一个Transaction实例下操作。在进行具体数据库操作时候,则使用tx.Session() API可以获得当前事务所持有的session会话,从而进行Get(),Find(),Execute()等具体数据库操作。


Expand Down Expand Up @@ -1152,4 +1199,4 @@ engine.ImportFile(fpath string)


## 讨论
请加入QQ群:280360085 进行讨论。API设计相关建议可联系本人QQ:50892683
请加入原版xorm QQ群一:280360085(已满)QQ群二:795010183 进行讨论。本定制版API设计相关建议可联系本人QQ:50892683
28 changes: 28 additions & 0 deletions context_cache.go
@@ -0,0 +1,28 @@
// 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]
}
6 changes: 6 additions & 0 deletions session.go
Expand Up @@ -107,6 +107,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
Expand Down
21 changes: 20 additions & 1 deletion session_get.go
Expand Up @@ -7,6 +7,7 @@ package xorm
import (
"database/sql"
"errors"
"fmt"
"reflect"
"strconv"

Expand Down Expand Up @@ -77,7 +78,25 @@ 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) {
Expand Down
56 changes: 56 additions & 0 deletions session_get_test.go
Expand Up @@ -319,3 +319,59 @@ 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)
}
2 changes: 2 additions & 0 deletions statement.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion xorm.go
Expand Up @@ -19,7 +19,7 @@ import (

const (
// Version show the xorm's version
Version string = "0.7.0.0918"
Version string = "0.7.0.0926"
)

func regDrvsNDialects() bool {
Expand Down

0 comments on commit aa450dd

Please sign in to comment.