Documentation ¶
Overview ¶
Example ¶
package main import ( "database/sql" "fmt" "os" "sync" "time" "gitee.com/go-better/dev/db/pg" "gitee.com/go-better/dev/db/pgcache" loggerPkg "gitee.com/go-better/dev/debug/logger" ) var dbUrl = "postgres://postgres:postgres@localhost/postgres?sslmode=disable" var testDB = connectDB(dbUrl) var logger = loggerPkg.New(os.Stderr) type Student struct { Id int64 Name string Class string UpdatedAt time.Time } func (s Student) String() string { return fmt.Sprintf(`{%d %s %s %s}`, s.Id, s.Name, s.Class, s.UpdatedAt.Format(`2006-01-02 15:04:05 Z0700`)) } func main() { initStudentsTable() var studentsMap = make(map[int64]Student) var classesMap = make(map[string][]Student) var mutex sync.RWMutex dbCache, err := pgcache.New(dbUrl, pg.New(testDB, time.Second), logger) if err != nil { panic(err) } _, err = dbCache.Add(&pgcache.Table{ Name: "students", RowStruct: Student{}, Datas: []*pgcache.Data{ { RWMutex: &mutex, DataPtr: &studentsMap, MapKeys: []string{"Id"}, }, { RWMutex: &mutex, DataPtr: &classesMap, MapKeys: []string{"Class"}, SortedSetUniqueKey: []string{"Id"}, }, }, }) if err != nil { panic(err) } // from now on, studentsMap and classesMap is always synchronized with students table. fmt.Println(`init:`) fmt.Println(studentsMap) fmt.Println(classesMap) // even you insert some rows. testInsert(studentsMap, classesMap) // even you update some rows. testUpdate(studentsMap, classesMap) // even you delete some rows. testDelete(studentsMap, classesMap) dbCache.RemoveAll() } func initStudentsTable() { if _, err := testDB.Exec(` SET TIME ZONE 'PRC'; DROP TABLE IF EXISTS students; CREATE TABLE IF NOT EXISTS students ( id bigserial, name text, class text, updated_at timestamptz ); INSERT INTO students (id, name, class, updated_at) VALUES (1, '李雷', '初三1班', '2003-10-1T09:10:10+08:00'), (2, '韩梅梅', '初三1班', '2003-10-1T09:10:20+08:00'); `); err != nil { panic(err) } } func testInsert(studentsMap map[int64]Student, classesMap map[string][]Student) { if _, err := testDB.Exec(` INSERT INTO students (id, name, class, updated_at) VALUES (3, 'Lily', '初三2班', '2003-10-1T09:10:30+08:00'), (4, 'Lucy', '初三2班', '2003-10-1T09:10:30+08:00'); `); err != nil { panic(err) } time.Sleep(10 * time.Millisecond) fmt.Println(`after INSERT:`) fmt.Println(studentsMap) fmt.Println(classesMap) } func testUpdate(studentsMap map[int64]Student, classesMap map[string][]Student) { if _, err := testDB.Exec( `UPDATE students SET "class" = '初三2班', updated_at = '2003-10-1 09:10:40+08:00'`, ); err != nil { panic(err) } time.Sleep(10 * time.Millisecond) fmt.Println(`after UPDATE:`) fmt.Println(studentsMap) fmt.Println(classesMap) } func testDelete(studentsMap map[int64]Student, classesMap map[string][]Student) { if _, err := testDB.Exec(`DELETE FROM students WHERE id in (3, 4)`); err != nil { panic(err) } time.Sleep(10 * time.Millisecond) fmt.Println(`after DELETE:`) fmt.Println(studentsMap) fmt.Println(classesMap) } func connectDB(dbUrl string) *sql.DB { db, err := sql.Open(`postgres`, dbUrl) if err != nil { panic(err) } return db }
Output: init: map[1:{1 李雷 初三1班 2003-10-01 09:10:10 +0800} 2:{2 韩梅梅 初三1班 2003-10-01 09:10:20 +0800}] map[初三1班:[{1 李雷 初三1班 2003-10-01 09:10:10 +0800} {2 韩梅梅 初三1班 2003-10-01 09:10:20 +0800}]] after INSERT: map[1:{1 李雷 初三1班 2003-10-01 09:10:10 +0800} 2:{2 韩梅梅 初三1班 2003-10-01 09:10:20 +0800} 3:{3 Lily 初三2班 2003-10-01 09:10:30 +0800} 4:{4 Lucy 初三2班 2003-10-01 09:10:30 +0800}] map[初三1班:[{1 李雷 初三1班 2003-10-01 09:10:10 +0800} {2 韩梅梅 初三1班 2003-10-01 09:10:20 +0800}] 初三2班:[{3 Lily 初三2班 2003-10-01 09:10:30 +0800} {4 Lucy 初三2班 2003-10-01 09:10:30 +0800}]] after UPDATE: map[1:{1 李雷 初三2班 2003-10-01 09:10:40 +0800} 2:{2 韩梅梅 初三2班 2003-10-01 09:10:40 +0800} 3:{3 Lily 初三2班 2003-10-01 09:10:40 +0800} 4:{4 Lucy 初三2班 2003-10-01 09:10:40 +0800}] map[初三2班:[{1 李雷 初三2班 2003-10-01 09:10:40 +0800} {2 韩梅梅 初三2班 2003-10-01 09:10:40 +0800} {3 Lily 初三2班 2003-10-01 09:10:40 +0800} {4 Lucy 初三2班 2003-10-01 09:10:40 +0800}]] after DELETE: map[1:{1 李雷 初三2班 2003-10-01 09:10:40 +0800} 2:{2 韩梅梅 初三2班 2003-10-01 09:10:40 +0800}] map[初三2班:[{1 李雷 初三2班 2003-10-01 09:10:40 +0800} {2 韩梅梅 初三2班 2003-10-01 09:10:40 +0800}]]
Index ¶
- func Field2Column(str string) string
- type DB
- type DBQuerier
- type Data
- type Logger
- type Table
- func (t *Table) Clear()
- func (t *Table) ConnLoss(table string)
- func (t *Table) Create(table string, content []byte)
- func (t *Table) Delete(table string, content []byte)
- func (t *Table) Error(err interface{})
- func (t *Table) GetDatas() []manage.Data
- func (t *Table) Init(table string)
- func (t *Table) Reload() error
- func (t *Table) Remove(rows interface{})
- func (t *Table) Save(rows interface{})
- func (t *Table) Update(table string, oldContent, newContent []byte)
Examples ¶
- Package
- Data (Init_flags1)
- Data (Init_flags2)
- Data (Init_invalidDataPtr_1)
- Data (Init_invalidDataPtr_2)
- Data (Init_invalidMapKeys_1)
- Data (Init_invalidMapKeys_2)
- Data (Init_invalidMapKeys_3)
- Data (Init_invalidMapKeys_4)
- Data (Init_invalidMapKeys_5)
- Data (Init_invalidPrecond_1)
- Data (Init_invalidPrecond_2)
- Data (Init_invalidSortedSetUniqueKey_1)
- Data (Init_invalidSortedSetUniqueKey_2)
- Data (Init_invalidSortedSetUniqueKey_3)
- Data (Init_invalidSortedSetUniqueKey_4)
- Data (Init_invalidSortedSetUniqueKey_5)
- Data (Init_invalidValue_1)
- Data (Init_invalidValue_2)
- Data (Init_invalidValue_3)
- Data (Init_nilMutex)
- Data (Init_validPrecond_1)
- Data (Init_validPrecond_2)
- Data (Precond_1)
- Data (Precond_2)
- Data (Save_remove_clear)
- Data (Save_remove_clear_sortedset)
- Data (Save_remove_clear_sortedset_2)
- Data (Save_remove_clear_sortedset_3)
- Table
- Table (Init)
- Table (Init_bigColumns)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Data ¶
type Data struct { *sync.RWMutex // DataPtr is a pointer to a map or slice to store data, required. DataPtr interface{} // MapKeys is the field names to get map keys from row struct, required if DataPtr is a map. MapKeys []string // Value is the field name to get map or slice value from row struct. // If it's empty, the whole row struct is used. Value string // If the DataPtr or map value is a slice, it's used as sorted set. If it's a sorted set of struct, // SortedSetUniqueKey is required, it specifies the fields used as unique key. SortedSetUniqueKey []string // Preprocess is optional. It's a method name of row struct. It should be of "func ()" form. // It is called before Precond method is called. Preprocess string // Precond is optional. It's a method name of row struct. It should be of "func () bool" form. // It is called before handling, if the return value is false, no handling(save or remove) is performed. Precond string // contains filtered or unexported fields }
Example (Init_flags1) ¶
mutex := sync.RWMutex{} var m map[int]map[string][]*int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "Score", } fmt.Println(d.init(reflect.TypeOf(Score{}))) fmt.Println(d.isSortedSets, d.realValueIsPointer)
Output: <nil> true true
Example (Init_flags2) ¶
mutex := sync.RWMutex{} var m map[int][]*Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, SortedSetUniqueKey: []string{"Subject"}, } fmt.Println(d.init(reflect.TypeOf(Score{}))) fmt.Println(d.isSortedSets, d.realValueIsPointer)
Output: <nil> true true
Example (Init_invalidDataPtr_1) ¶
mutex := sync.RWMutex{} d := Data{RWMutex: &mutex, DataPtr: map[int]int{}} fmt.Println(d.init(nil))
Output: Data.DataPtr should be a non nil pointer to a map or slice.
Example (Init_invalidDataPtr_2) ¶
mutex := sync.RWMutex{} var p *map[int]int d := Data{RWMutex: &mutex, DataPtr: p} fmt.Println(d.init(nil))
Output: Data.DataPtr should be a non nil pointer to a map or slice.
Example (Init_invalidMapKeys_1) ¶
mutex := sync.RWMutex{} var m map[int]int d := Data{RWMutex: &mutex, DataPtr: &m} fmt.Println(d.init(nil))
Output: Data.DataPtr is a 1 layers map, but Data.MapKeys has 0 field.
Example (Init_invalidMapKeys_2) ¶
mutex := sync.RWMutex{} var m map[int]map[string]int d := Data{RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject", "Other"}} fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.DataPtr is a 2 layers map, but Data.MapKeys has 3 field.
Example (Init_invalidMapKeys_3) ¶
mutex := sync.RWMutex{} var m map[int]map[string]int d := Data{RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"Student", "Subject"}} fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.MapKeys[0]: Student, no such field in row struct.
Example (Init_invalidMapKeys_4) ¶
mutex := sync.RWMutex{} var m map[int]map[string]int d := Data{RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"Subject", "StudentId"}} fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.MapKeys[0]: Subject, type string is not assignable to int.
Example (Init_invalidMapKeys_5) ¶
mutex := sync.RWMutex{} var s []int d := Data{RWMutex: &mutex, DataPtr: &s, MapKeys: []string{"Subject", "StudentId"}} fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.DataPtr is a slice, so Data.MapKeys should be empty.
Example (Init_invalidPrecond_1) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "None", } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.Precond: None, no such method for the row struct.
Example (Init_invalidPrecond_2) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "Other", } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.Precond: Other, should be of "func () bool" form.
Example (Init_invalidSortedSetUniqueKey_1) ¶
mutex := sync.RWMutex{} var m map[int]map[string]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "Score", SortedSetUniqueKey: []string{"Other"}, } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.SortedSetUniqueKey should be empty.
Example (Init_invalidSortedSetUniqueKey_2) ¶
mutex := sync.RWMutex{} var m map[int]map[string][]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "Score", SortedSetUniqueKey: []string{"Other"}, } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.SortedSetUniqueKey should be empty.
Example (Init_invalidSortedSetUniqueKey_3) ¶
mutex := sync.RWMutex{} var m map[int][]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, SortedSetUniqueKey: []string{}, } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.SortedSetUniqueKey should not be empty.
Example (Init_invalidSortedSetUniqueKey_4) ¶
mutex := sync.RWMutex{} var m map[int][]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, SortedSetUniqueKey: []string{"Other"}, } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.SortedSetUniqueKey[0]: Other, no such field in value struct.
Example (Init_invalidSortedSetUniqueKey_5) ¶
mutex := sync.RWMutex{} type score2 struct { Score ScoreFloat float32 } var m map[int][]score2 d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, SortedSetUniqueKey: []string{"ScoreFloat"}, } fmt.Println(d.init(reflect.TypeOf(score2{})))
Output: Data.SortedSetUniqueKey[0]: ScoreFloat, should be a integer or string type.
Example (Init_invalidValue_1) ¶
mutex := sync.RWMutex{} var m map[int]map[string]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "theScore", } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.Value: theScore, no such field in row struct.
Example (Init_invalidValue_2) ¶
mutex := sync.RWMutex{} var m map[int]map[string]float32 d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "Score", } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.Value: Score, type int is not assignable to float32.
Example (Init_invalidValue_3) ¶
mutex := sync.RWMutex{} var s []*Score d := Data{ RWMutex: &mutex, DataPtr: &s, Value: "Score", } fmt.Println(d.init(reflect.TypeOf(Score{})))
Output: Data.Value: Score, type int is not assignable to *pgcache.Score.
Example (Init_nilMutex) ¶
d := Data{} fmt.Println(d.init(nil))
Output: Data.RWMutex is nil.
Example (Init_validPrecond_1) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "Valid", } fmt.Println(d.init(reflect.TypeOf(Score{}))) fmt.Println(d.precondMethodIndex)
Output: <nil> 1
Example (Init_validPrecond_2) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "Valid2", } fmt.Println(d.init(reflect.TypeOf(Score{}))) fmt.Println(d.precondMethodIndex)
Output: <nil> 2
Example (Precond_1) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "Valid", } d.init(reflect.TypeOf(Score{})) fmt.Println( d.precond(reflect.ValueOf(&Score{Score: 1}).Elem()), d.precond(reflect.ValueOf(&Score{Score: -1}).Elem()), )
Output: true false
Example (Precond_2) ¶
mutex := sync.RWMutex{} var m map[int]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Precond: "Valid2", } d.init(reflect.TypeOf(Score{})) fmt.Println( d.precond(reflect.ValueOf(&Score{Score: 1}).Elem()), d.precond(reflect.ValueOf(&Score{Score: -1}).Elem()), )
Output: true false
Example (Save_remove_clear) ¶
mutex := sync.RWMutex{} var m map[int]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Value: "Score", Precond: "Valid", } d.init(reflect.TypeOf(Score{})) d.clear() rows := reflect.ValueOf([]Score{ {StudentId: 1001, Score: 98}, {StudentId: 1002, Score: 101}, {StudentId: 1003, Score: 99}, {StudentId: 1002, Score: 100}, }) for i := 0; i < rows.Len(); i++ { d.save(rows.Index(i)) } fmt.Println(m) rows = reflect.ValueOf([]Score{ {StudentId: 1002, Score: 100}, {StudentId: 1004}, }) for i := 0; i < rows.Len(); i++ { d.remove(rows.Index(i)) } fmt.Println(m) d.clear() fmt.Println(m)
Output: map[1001:98 1002:100 1003:99] map[1001:98 1003:99] map[]
Example (Save_remove_clear_sortedset) ¶
mutex := sync.RWMutex{} var m map[int][]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, Value: "Score", Precond: "Valid", } d.init(reflect.TypeOf(Score{})) rows := reflect.ValueOf([]Score{ {StudentId: 1001, Score: 98}, {StudentId: 1001, Score: 99}, {StudentId: 1002, Score: 90}, {StudentId: 1003, Score: 99}, {StudentId: 1002, Score: 91}, {StudentId: 1003, Score: 100}, {StudentId: 1001, Score: 99}, }) for i := 0; i < rows.Len(); i++ { d.save(rows.Index(i)) } fmt.Println(m) rows = reflect.ValueOf([]Score{ {StudentId: 1002, Score: 90}, {StudentId: 1002, Score: 91}, {StudentId: 1003, Score: 100}, {StudentId: 1004}, }) for i := 0; i < rows.Len(); i++ { d.remove(rows.Index(i)) } fmt.Println(m) d.clear() fmt.Println(m)
Output: map[1001:[98 99] 1002:[90 91] 1003:[99 100]] map[1001:[98 99] 1003:[99]] map[]
Example (Save_remove_clear_sortedset_2) ¶
mutex := sync.RWMutex{} var m map[int][]Score d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId"}, SortedSetUniqueKey: []string{"Subject"}, } d.init(reflect.TypeOf(Score{})) rows := reflect.ValueOf([]Score{ {StudentId: 1001, Subject: "语文", Score: 98}, {StudentId: 1001, Subject: "语文", Score: 99}, {StudentId: 1002, Subject: "数学", Score: 90}, {StudentId: 1003, Subject: "语文", Score: 99}, {StudentId: 1002, Subject: "数学", Score: 91}, {StudentId: 1003, Subject: "数学", Score: 100}, }) for i := 0; i < rows.Len(); i++ { d.save(rows.Index(i)) } fmt.Println(m) rows = reflect.ValueOf([]Score{ {StudentId: 1001, Subject: "语文", Score: 90}, {StudentId: 1002, Subject: "数学", Score: 90}, {StudentId: 1003, Subject: "数学"}, {StudentId: 1004, Subject: "语文"}, }) for i := 0; i < rows.Len(); i++ { d.remove(rows.Index(i)) } fmt.Println(m) d.clear() fmt.Println(m)
Output: map[1001:[{1001 语文 99}] 1002:[{1002 数学 91}] 1003:[{1003 数学 100} {1003 语文 99}]] map[1003:[{1003 语文 99}]] map[]
Example (Save_remove_clear_sortedset_3) ¶
mutex := sync.RWMutex{} var m map[int]map[string][]int d := Data{ RWMutex: &mutex, DataPtr: &m, MapKeys: []string{"StudentId", "Subject"}, Value: "Score", } d.init(reflect.TypeOf(Score{})) rows := reflect.ValueOf([]Score{ {StudentId: 1001, Subject: "语文", Score: 98}, {StudentId: 1001, Subject: "语文", Score: 99}, {StudentId: 1002, Subject: "数学", Score: 91}, {StudentId: 1003, Subject: "语文", Score: 99}, {StudentId: 1002, Subject: "数学", Score: 90}, {StudentId: 1003, Subject: "数学", Score: 100}, }) for i := 0; i < rows.Len(); i++ { d.save(rows.Index(i)) } fmt.Println(m) rows = reflect.ValueOf([]Score{ {StudentId: 1002, Subject: "数学", Score: 90}, {StudentId: 1002, Subject: "数学", Score: 91}, {StudentId: 1004, Subject: "语文"}, }) for i := 0; i < rows.Len(); i++ { d.remove(rows.Index(i)) } fmt.Println(m) m = make(map[int]map[string][]int) fmt.Println(d.dataV)
Output: map[1001:map[语文:[98 99]] 1002:map[数学:[90 91]] 1003:map[数学:[100] 语文:[99]]] map[1001:map[语文:[98 99]] 1002:map[] 1003:map[数学:[100] 语文:[99]]] map[]
type Logger ¶
type Logger interface { Error(args ...interface{}) Errorf(format string, args ...interface{}) }
type Table ¶
type Table struct { // The name of the table to cache, required. Name string // The struct to receive a table row. RowStruct interface{} // The columns of the table to cache. It's got from the pg_notify payload, it must be less than // 8000 bytes, use "BigColumns" if necessarry. // If empty, the fields of "RowStruct" which is not "BigColumns" are used. // The field name is converted to underscore style, and field with `json:"-"` tag is ignored. Columns string // The big columns of the table to cache. It's got by a seperate query. // Warning: when update, it will not be set on the old value. BigColumns string // The unique fields to load "BigColumns" from db. If empty, and "RowStruct" has a "Id" Field, // it's used as "BigColumnsLoadKeys". BigColumnsLoadKeys []string // The sql used to load initial data when a table is cached, or reload table data when the db // connection lost. If empty, "Columns" and "BigColumns" is used to make a SELECT sql FROM "NAME". LoadSql string // Datas is the maps to store table rows. Datas []*Data // contains filtered or unexported fields }
A Handler to cache table data.
Example ¶
var m1 map[int]map[string]int var m2 map[string]map[int]int var mutex sync.RWMutex t := &Table{ Name: "scores", RowStruct: Score{}, Datas: []*Data{ {RWMutex: &mutex, DataPtr: &m1, MapKeys: []string{"StudentId", "Subject"}, Value: "Score"}, {RWMutex: &mutex, DataPtr: &m2, MapKeys: []string{"Subject", "StudentId"}, Value: "Score"}, }, } t.init("db", testQuerier{}, testLogger) t.Init("") fmt.Println(m1, m2) t.Create("", []byte(`{"StudentId": 1001, "Subject": "语文", "Score": 95}`)) fmt.Println(m1, m2) t.Update("", []byte(`{"StudentId": 1001, "Subject": "语文", "Score": 95}`), []byte(`{"StudentId": 1001, "Subject": "数学", "Score": 96}`), ) fmt.Println(m1, m2) t.Delete("", []byte(`{"StudentId": 1001, "Subject": "数学", "Score": 96}`), ) fmt.Println(m1, m2) t.Clear() fmt.Println(m1, m2) t.Create("", []byte(`{"StudentId": 1001, "Subject": "语文", "Score": 95}`)) fmt.Println(m1, m2)
Output: map[1000:map[语文:90]] map[语文:map[1000:90]] map[1000:map[语文:90] 1001:map[语文:95]] map[语文:map[1000:90 1001:95]] map[1000:map[语文:90] 1001:map[数学:96]] map[数学:map[1001:96] 语文:map[1000:90]] map[1000:map[语文:90] 1001:map[]] map[数学:map[] 语文:map[1000:90]] map[] map[] map[1001:map[语文:95]] map[语文:map[1001:95]]
Example (Init) ¶
t := Table{ Name: "scores", RowStruct: struct { Score Z bool `json:"-"` }{}, } t.init("", testQuerier{}, testLogger) fmt.Println(t.Columns) fmt.Println(t.BigColumns) fmt.Println(t.LoadSql)
Output: student_id,subject,score SELECT student_id,subject,score FROM scores
Example (Init_bigColumns) ¶
t := Table{ Name: "scores", RowStruct: Score{}, BigColumns: "score", BigColumnsLoadKeys: []string{"StudentId", "Subject"}, } t.init("", testQuerier{}, testLogger) fmt.Println(t.Columns) fmt.Println(t.BigColumns) fmt.Println(t.bigColumnsLoadSql) fmt.Println(t.LoadSql)
Output: student_id,subject score SELECT score FROM scores WHERE student_id = %s AND subject = %s SELECT student_id,subject ,score FROM scores
Directories ¶
Path | Synopsis |
---|---|
vim: set ft=html:
|
vim: set ft=html: |
适用于低频修改且量小的数据,单线程操作。
|
适用于低频修改且量小的数据,单线程操作。 |
Click to show internal directories.
Click to hide internal directories.