Sfoglia il codice sorgente

Get adds context cache feature

* 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
xormplus 7 anni fa
parent
commit
aa450dde4b
7 ha cambiato i file con 162 aggiunte e 4 eliminazioni
  1. 49 2
      README.md
  2. 28 0
      context_cache.go
  3. 6 0
      session.go
  4. 20 1
      session_get.go
  5. 56 0
      session_get_test.go
  6. 2 0
      statement.go
  7. 1 1
      xorm.go

+ 49 - 2
README.md

@@ -1,5 +1,5 @@
 # xorm
-xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。请加入原版xorm QQ群:280360085 进行讨论。本定制版API设计相关建议可联系本人QQ:50892683
+xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
 
 ## 说明
 
@@ -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)
+* 上下文缓存支持
 
 ## 驱动支持
 
@@ -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()
@@ -506,6 +551,8 @@ if err != nil {
 }
 ```
 
+* 嵌套事务模型
+
 在实际业务开发过程中我们会发现依然还有一些特殊场景,我们需要借助嵌套事务来进行事务处理。本xorm版本也提供了嵌套事务的支持。当使用嵌套事务模型进行事务处理时,同样也需要创建Session对象,与使用简单事务模型进行事务处理不同在于,使用session.Begin()创建简单事务时,直接在同一个session下操作,而使用嵌套事务模型进行事务处理时候,使用session.BeginTrans()创建嵌套事务时,将返回Transaction实例,后续操作则在同一个Transaction实例下操作。在进行具体数据库操作时候,则使用tx.Session() API可以获得当前事务所持有的session会话,从而进行Get(),Find(),Execute()等具体数据库操作。
 
 
@@ -1152,4 +1199,4 @@ engine.ImportFile(fpath string)
 
 
 ## 讨论
-请加入QQ群:280360085 进行讨论。API设计相关建议可联系本人QQ:50892683
+请加入原版xorm QQ群:280360085(已满)QQ群二:795010183 进行讨论。本定制版API设计相关建议可联系本人QQ:50892683

+ 28 - 0
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 - 0
session.go

@@ -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

+ 20 - 1
session_get.go

@@ -7,6 +7,7 @@ package xorm
 import (
 	"database/sql"
 	"errors"
+	"fmt"
 	"reflect"
 	"strconv"
 
@@ -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) {

+ 56 - 0
session_get_test.go

@@ -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 - 0
statement.go

@@ -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

+ 1 - 1
xorm.go

@@ -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 {