performance improved for db.ScanStruct2

This commit is contained in:
Lunny Xiao 2014-01-26 00:05:37 +08:00
parent 8c5763bb3a
commit f1a4636699
3 changed files with 78 additions and 7 deletions

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"errors"
"reflect"
"sync"
)
type DB struct {
@ -13,7 +14,7 @@ type DB struct {
func Open(driverName, dataSourceName string) (*DB, error) {
db, err := sql.Open(driverName, dataSourceName)
return &DB{db, &SnakeMapper{}}, err
return &DB{db, NewCacheMapper(&SnakeMapper{})}, err
}
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
@ -59,6 +60,18 @@ func (rs *Rows) ScanStruct(dest ...interface{}) error {
return rs.Rows.Scan(newDest...)
}
type EmptyScanner struct {
}
func (EmptyScanner) Scan(src interface{}) error {
return nil
}
var (
fieldCache = make(map[reflect.Type]map[string]int)
fieldCacheMutex sync.RWMutex
)
// scan data to a struct's pointer according field name
func (rs *Rows) ScanStruct2(dest interface{}) error {
vv := reflect.ValueOf(dest)
@ -72,14 +85,27 @@ func (rs *Rows) ScanStruct2(dest interface{}) error {
}
vvv := vv.Elem()
newDest := make([]interface{}, len(cols))
t := vvv.Type()
fieldCacheMutex.RLock()
cache, ok := fieldCache[t]
fieldCacheMutex.RUnlock()
if !ok {
cache = make(map[string]int)
for i := 0; i < vvv.NumField(); i++ {
cache[vvv.Type().Field(i).Name] = i
}
fieldCacheMutex.Lock()
fieldCache[t] = cache
fieldCacheMutex.Unlock()
}
newDest := make([]interface{}, len(cols))
var v EmptyScanner
for j, name := range cols {
f := vvv.FieldByName(rs.Mapper.Table2Obj(name))
if f.IsValid() {
newDest[j] = f.Addr().Interface()
if i, ok := cache[rs.Mapper.Table2Obj(name)]; ok {
newDest[j] = vvv.Field(i).Addr().Interface()
} else {
var v interface{}
newDest[j] = &v
}
}

View File

@ -125,7 +125,7 @@ func BenchmarkStruct2Query(b *testing.B) {
}
}
db.Mapper = &SnakeMapper{}
db.Mapper = NewCacheMapper(&SnakeMapper{})
b.StartTimer()
for i := 0; i < b.N; i++ {

View File

@ -2,6 +2,7 @@ package core
import (
"strings"
"sync"
)
// name translation between struct, fields names and table, column names
@ -10,6 +11,50 @@ type IMapper interface {
Table2Obj(string) string
}
type CacheMapper struct {
oriMapper IMapper
obj2tableCache map[string]string
obj2tableMutex sync.RWMutex
table2objCache map[string]string
table2objMutex sync.RWMutex
}
func NewCacheMapper(mapper IMapper) *CacheMapper {
return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string),
table2objCache: make(map[string]string),
}
}
func (m *CacheMapper) Obj2Table(o string) string {
m.obj2tableMutex.RLock()
t, ok := m.obj2tableCache[o]
m.obj2tableMutex.RUnlock()
if ok {
return t
}
t = m.oriMapper.Obj2Table(o)
m.obj2tableMutex.Lock()
m.obj2tableCache[o] = t
m.obj2tableMutex.Unlock()
return t
}
func (m *CacheMapper) Table2Obj(t string) string {
m.table2objMutex.RLock()
o, ok := m.table2objCache[t]
m.table2objMutex.RUnlock()
if ok {
return o
}
o = m.oriMapper.Table2Obj(t)
m.table2objMutex.Lock()
m.table2objCache[t] = o
m.table2objMutex.Unlock()
return o
}
// SameMapper implements IMapper and provides same name between struct and
// database table
type SameMapper struct {