diff --git a/TODO b/TODO index 2edb18ee6..a4aaa2f40 100644 --- a/TODO +++ b/TODO @@ -15,6 +15,7 @@ gpage分页增加对自定义后缀的支持,如:2.html, 2.php等等; gvalid包增加tag与校验规则绑定的支持特性; ghttp获取参数支持直接转struct功能; map转struct增加对tag的支持; +gdb增加查询缓存特性; DONE: diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index a718f8c6b..95db1948a 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -16,6 +16,7 @@ import ( _ "github.com/go-sql-driver/mysql" "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/container/gring" + "gitee.com/johng/gf/g/os/gcache" ) const ( @@ -83,13 +84,14 @@ type Link interface { // 数据库链接对象 type Db struct { - link Link // 底层数据库类型管理对象 - master *sql.DB // 实例化数据库链接(master) - slave *sql.DB // 实例化数据库链接(slave,可能会与master相同) - charl string // SQL安全符号(左) - charr string // SQL安全符号(右) - debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 - sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 + link Link // 底层数据库类型管理对象 + master *sql.DB // 实例化数据库链接(master) + slave *sql.DB // 实例化数据库链接(slave,可能会与master相同) + charl string // SQL安全符号(左) + charr string // SQL安全符号(右) + debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 + sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 + cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存 } // 执行的SQL对象 @@ -222,6 +224,7 @@ func newDb (masterNode *ConfigNode, slaveNode *ConfigNode) (*Db, error) { charl : link.getQuoteCharLeft(), charr : link.getQuoteCharRight(), debug : gtype.NewBool(), + cache : gcache.New(), }, nil } diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index 8a32d77be..0b7b1224e 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -28,6 +28,9 @@ type Model struct { limit int // 分页条数 data interface{} // 操作记录(支持Map/List/string类型) batch int // 批量操作条数 + cacheEnabled bool // 当前SQL操作是否开启查询缓存功能 + cacheTime int // 查询缓存时间 + cacheName string // 查询缓存名称 } // 链式操作,数据表字段,可支持多个表,以半角逗号连接 @@ -243,13 +246,22 @@ func (md *Model) Batch(batch int) *Model { return md } +// 查询缓存/清除缓存操作,需要注意的是,事务查询不支持缓存。 +// 当time < 0时表示清除缓存, time=0时表示不过期, time > 0时表示过期时间,time过期时间单位:秒; +// name表示自定义的缓存名称,便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称), +// 例如:查询缓存时设置名称,清理缓存时可以给定清理的缓存名称进行精准清理。 +func (md *Model) Cache(time int, name ... string) *Model { + md.cacheEnabled = true + md.cacheTime = time + if len(name) > 0 { + md.cacheName = name[0] + } + return md +} + // 链式操作,select func (md *Model) Select() (Result, error) { - if md.tx == nil { - return md.db.GetAll(md.getFormattedSql(), md.whereArgs...) - } else { - return md.tx.GetAll(md.getFormattedSql(), md.whereArgs...) - } + return md.getAll(md.getFormattedSql(), md.whereArgs...) } // 链式操作,查询所有记录 @@ -269,15 +281,6 @@ func (md *Model) One() (Record, error) { return nil, nil } -// 链式操作,查询单条记录,并自动转换为struct对象 -func (md *Model) Struct(obj interface{}) error { - one, err := md.One() - if err != nil { - return err - } - return one.ToStruct(obj) -} - // 链式操作,查询字段值 func (md *Model) Value() (Value, error) { one, err := md.One() @@ -290,8 +293,17 @@ func (md *Model) Value() (Value, error) { return nil, nil } +// 链式操作,查询单条记录,并自动转换为struct对象 +func (md *Model) Struct(obj interface{}) error { + one, err := md.One() + if err != nil { + return err + } + return one.ToStruct(obj) +} + // 链式操作,查询数量,fields可以为空,也可以自定义查询字段, -// 当给定自定义查询字段时,该字段必须为数量结果,否则会引起歧义,如:Fields("COUNT(id)") +// 当给定自定义查询字段时,该字段必须为数量结果,否则会引起歧义,使用如:md.Fields("COUNT(id)") func (md *Model) Count() (int, error) { if md.fields == "" || md.fields == "*" { md.fields = "COUNT(1)" @@ -300,11 +312,43 @@ func (md *Model) Count() (int, error) { if len(md.groupBy) > 0 { s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s) } - if md.tx == nil { - return md.db.GetCount(s, md.whereArgs...) - } else { - return md.tx.GetCount(s, md.whereArgs...) + list, err := md.getAll(s, md.whereArgs...) + if err != nil { + return 0, err } + if len(list) > 0 { + for _, v := range list[0] { + return gconv.Int(v), nil + } + } + return 0, nil +} + +// 查询操作,对底层SQL操作的封装 +func (md *Model) getAll(sql string, args...interface{}) (Result, error) { + var err error + var result Result + var cacheKey string + // 查询缓存查询处理 + if md.cacheEnabled && md.tx == nil { + cacheKey = md.cacheName + if len(cacheKey) == 0 { + cacheKey = sql + } + if v := md.db.cache.Get(cacheKey); v != nil { + return v.(Result), nil + } + } + if md.tx == nil { + result, err = md.db.GetAll(sql, args...) + } else { + result, err = md.tx.GetAll(sql, args...) + } + // 查询缓存保存处理 + if len(cacheKey) > 0 && md.cacheTime >= 0 { + md.db.cache.Set(cacheKey, result, md.cacheTime*1000) + } + return result, err } // 格式化当前输入参数,返回可执行的SQL语句(不带参数) @@ -326,4 +370,4 @@ func (md *Model) getFormattedSql() string { s += fmt.Sprintf(" LIMIT %d, %d", md.start, md.limit) } return s -} \ No newline at end of file +} diff --git a/g/os/gcache/gcache.go b/g/os/gcache/gcache.go index 487a8d454..21eb27aa7 100644 --- a/g/os/gcache/gcache.go +++ b/g/os/gcache/gcache.go @@ -72,12 +72,12 @@ func SetCap(cap int) { } // (使用全局KV缓存对象)设置kv缓存键值对,过期时间单位为毫秒 -func Set(key string, value interface{}, expire int64) { +func Set(key string, value interface{}, expire int) { cache.Set(key, value, expire) } // (使用全局KV缓存对象)批量设置kv缓存键值对,过期时间单位为毫秒 -func BatchSet(data map[string]interface{}, expire int64) { +func BatchSet(data map[string]interface{}, expire int) { cache.BatchSet(data, expire) } @@ -97,7 +97,7 @@ func BatchRemove(keys []string) { } // 基于内存缓存的锁,锁成功返回true,失败返回false,当失败时表示有其他的锁存在 -func Lock(key string, expire int64) bool { +func Lock(key string, expire int) bool { if v := cache.Get(key); v != nil { return false } @@ -160,7 +160,7 @@ func (c *Cache) getOrNewExpireSet(expire int64) *gset.StringSet { } // 设置kv缓存键值对,过期时间单位为毫秒,expire<=0表示不过期 -func (c *Cache) Set(key string, value interface{}, expire int64) { +func (c *Cache) Set(key string, value interface{}, expire int) { var e int64 if expire != 0 { e = gtime.Millisecond() + int64(expire) @@ -174,7 +174,7 @@ func (c *Cache) Set(key string, value interface{}, expire int64) { } // 批量设置 -func (c *Cache) BatchSet(data map[string]interface{}, expire int64) { +func (c *Cache) BatchSet(data map[string]interface{}, expire int) { var e int64 if expire != 0 { e = gtime.Millisecond() + int64(expire) @@ -190,7 +190,7 @@ func (c *Cache) BatchSet(data map[string]interface{}, expire int64) { } // 基于内存缓存的锁,锁成功返回true,失败返回false,当失败时表示有其他的锁存在 -func (c *Cache) Lock(key string, expire int64) bool { +func (c *Cache) Lock(key string, expire int) bool { if v := c.Get(key); v != nil { return false } diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index c92ffa371..e643968c2 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -51,6 +51,15 @@ func SetInterval(t time.Duration, callback func() bool) { }() } +// 设置当前进程全局的默认时区 +func SetTimeZone(zone string) error { + location, err := time.LoadLocation(zone) + if err == nil { + time.Local = location + } + return err +} + // 获取当前的纳秒数 func Nanosecond() int64 { return time.Now().UnixNano()