mirror of
https://gitee.com/johng/gf
synced 2026-07-06 21:45:34 +08:00
improve cache feature of package gdb.Model
This commit is contained in:
@ -206,15 +206,16 @@ func GetPrimaryKey(pointer interface{}) string {
|
||||
|
||||
// GetPrimaryKeyCondition returns a new where condition by primary field name.
|
||||
// The optional parameter <where> is like follows:
|
||||
// 123
|
||||
// []int{1, 2, 3}
|
||||
// "john"
|
||||
// []string{"john", "smith"}
|
||||
// g.Map{"id": g.Slice{1,2,3}}
|
||||
// g.Map{"id": 1, "name": "john"}
|
||||
// 123 => primary=123
|
||||
// []int{1, 2, 3} => primary IN(1,2,3)
|
||||
// "john" => primary='john'
|
||||
// []string{"john", "smith"} => primary IN('john','smith')
|
||||
// g.Map{"id": g.Slice{1,2,3}} => id IN(1,2,3)
|
||||
// g.Map{"id": 1, "name": "john"} => id=1 AND name='john'
|
||||
// etc.
|
||||
//
|
||||
// Note that it returns the given <where> parameter directly if there's the <primary> is empty.
|
||||
// Note that it returns the given <where> parameter directly if the <primary> is empty
|
||||
// or length of <where> > 1.
|
||||
func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
|
||||
if len(where) == 0 {
|
||||
return nil
|
||||
@ -231,6 +232,7 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
// Ignore the parameter <primary>.
|
||||
break
|
||||
|
||||
default:
|
||||
|
||||
@ -18,19 +18,26 @@ import (
|
||||
// If the parameter <duration> = 0, which means it never expires.
|
||||
// If the parameter <duration> > 0, which means it expires after <duration>.
|
||||
//
|
||||
// The optional parameter <name> is used to bind a name to the cache, which means you can later
|
||||
// control the cache like changing the <duration> or clearing the cache with specified <name>.
|
||||
// The optional parameter <name> is used to bind a name to the cache, which means you can
|
||||
// later control the cache like changing the <duration> or clearing the cache with specified
|
||||
// <name>.
|
||||
//
|
||||
// Note that, the cache feature is disabled if the model is operating on a transaction.
|
||||
// Note that, the cache feature is disabled if the model is performing select statement
|
||||
// on a transaction.
|
||||
func (m *Model) Cache(duration time.Duration, name ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.cacheDuration = duration
|
||||
if len(name) > 0 {
|
||||
model.cacheName = name[0]
|
||||
}
|
||||
// It does not support cache on transaction.
|
||||
if model.tx == nil {
|
||||
model.cacheEnabled = true
|
||||
}
|
||||
model.cacheEnabled = true
|
||||
return model
|
||||
}
|
||||
|
||||
// checkAndRemoveCache checks and removes the cache in insert/update/delete statement if
|
||||
// cache feature is enabled.
|
||||
func (m *Model) checkAndRemoveCache() {
|
||||
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
|
||||
m.db.GetCache().Remove(m.cacheName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -49,7 +48,8 @@ func (m *Model) Having(having interface{}, args ...interface{}) *Model {
|
||||
// WherePri does the same logic as Model.Where except that if the parameter <where>
|
||||
// is a single condition like int/string/float/slice, it treats the condition as the primary
|
||||
// key value. That is, if primary key is "id" and given <where> parameter as "123", the
|
||||
// WherePri function treats it as "id=123", but Model.Where treats it as string "123".
|
||||
// WherePri function treats the condition as "id=123", but Model.Where treats the condition
|
||||
// as string "123".
|
||||
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
|
||||
if len(args) > 0 {
|
||||
return m.Where(where, args...)
|
||||
@ -157,28 +157,3 @@ func (m *Model) Page(page, limit int) *Model {
|
||||
func (m *Model) ForPage(page, limit int) *Model {
|
||||
return m.Page(page, limit)
|
||||
}
|
||||
|
||||
// getAll does the query from database.
|
||||
func (m *Model) getAll(sql string, args ...interface{}) (result Result, err error) {
|
||||
cacheKey := ""
|
||||
// Retrieve from cache.
|
||||
if m.cacheEnabled {
|
||||
cacheKey = m.cacheName
|
||||
if len(cacheKey) == 0 {
|
||||
cacheKey = sql + "/" + gconv.String(args)
|
||||
}
|
||||
if v := m.db.GetCache().Get(cacheKey); v != nil {
|
||||
return v.(Result), nil
|
||||
}
|
||||
}
|
||||
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
|
||||
// Cache the result.
|
||||
if len(cacheKey) > 0 && err == nil {
|
||||
if m.cacheDuration < 0 {
|
||||
m.db.GetCache().Remove(cacheKey)
|
||||
} else {
|
||||
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ func (m *Model) All(where ...interface{}) (Result, error) {
|
||||
}
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
return m.getAll(
|
||||
return m.doGetAll(
|
||||
fmt.Sprintf(
|
||||
"SELECT %s FROM %s%s",
|
||||
m.db.QuoteString(m.fields),
|
||||
@ -268,7 +268,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
if len(m.groupBy) > 0 {
|
||||
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
|
||||
}
|
||||
list, err := m.getAll(s, conditionArgs...)
|
||||
list, err := m.doGetAll(s, conditionArgs...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -340,3 +340,28 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
|
||||
}
|
||||
return m.Scan(pointer)
|
||||
}
|
||||
|
||||
// doGetAll does the select statement on the database.
|
||||
func (m *Model) doGetAll(sql string, args ...interface{}) (result Result, err error) {
|
||||
cacheKey := ""
|
||||
// Retrieve from cache.
|
||||
if m.cacheEnabled && m.tx == nil {
|
||||
cacheKey = m.cacheName
|
||||
if len(cacheKey) == 0 {
|
||||
cacheKey = sql + "/" + gconv.String(args)
|
||||
}
|
||||
if v := m.db.GetCache().Get(cacheKey); v != nil {
|
||||
return v.(Result), nil
|
||||
}
|
||||
}
|
||||
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
|
||||
// Cache the result.
|
||||
if cacheKey != "" && err == nil {
|
||||
if m.cacheDuration < 0 {
|
||||
m.db.GetCache().Remove(cacheKey)
|
||||
} else {
|
||||
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -25,39 +25,39 @@ const (
|
||||
// If there's no field name for storing creating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameCreate(table ...string) string {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_CREATE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_CREATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameUpdate checks and returns the field name for record updating time.
|
||||
// If there's no field name for storing updating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameUpdate(table ...string) (field string) {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_UPDATE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_UPDATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameDelete checks and returns the field name for record deleting time.
|
||||
// If there's no field name for storing deleting time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameDelete(table ...string) (field string) {
|
||||
name := ""
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
tableName = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
tableName = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_DELETE)
|
||||
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_DELETE)
|
||||
}
|
||||
|
||||
// getSoftFieldName retrieves and returns the field name of the table for possible key.
|
||||
|
||||
@ -143,13 +143,6 @@ func (m *Model) getPrimaryKey() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// checkAndRemoveCache checks and remove the cache if necessary.
|
||||
func (m *Model) checkAndRemoveCache() {
|
||||
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
|
||||
m.db.GetCache().Remove(m.cacheName)
|
||||
}
|
||||
}
|
||||
|
||||
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
|
||||
// Note that this function does not change any attribute value of the <m>.
|
||||
//
|
||||
|
||||
@ -2213,6 +2213,63 @@ func Test_Model_Cache(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_200")
|
||||
})
|
||||
// transaction.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// make cache for id 3
|
||||
one, err := db.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_3")
|
||||
|
||||
r, err := db.Table(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
|
||||
err = db.Transaction(func(tx *gdb.TX) error {
|
||||
one, err := tx.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_300")
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
|
||||
one, err = db.Table(table).Cache(time.Second, "test3").FindOne(3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// make cache for id 4
|
||||
one, err := db.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_4")
|
||||
|
||||
r, err := db.Table(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
|
||||
err = db.Transaction(func(tx *gdb.TX) error {
|
||||
// Cache feature disabled.
|
||||
one, err := tx.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_400")
|
||||
// Update the cache.
|
||||
r, err := tx.Table(table).Data("passport", "user_4000").
|
||||
Cache(-1, "test4").WherePri(4).Update()
|
||||
t.Assert(err, nil)
|
||||
n, err := r.RowsAffected()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
// Read from db.
|
||||
one, err = db.Table(table).Cache(time.Second, "test4").FindOne(4)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], "user_4000")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Having(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user