Merge branch 'master' into dev
Conflicts: dialects/oracle.go dialects/postgres.go docs/QuickStart.md engine.go mysql.go session.go statement.go table.go tests/base_test.go xorm.go xorm/go.go xorm/reverse.go
This commit is contained in:
commit
5080b2b571
|
@ -1,14 +1,17 @@
|
|||
## Contributing to xorm
|
||||
|
||||
`xorm` has a backlog of pull requests, but contributions are still very
|
||||
`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very
|
||||
much welcome. You can help with patch review, submitting bug reports,
|
||||
or adding new functionality. There is no formal style guide, but
|
||||
please conform to the style of existing code and general Go formatting
|
||||
conventions when submitting patches.
|
||||
|
||||
* [fork a repo](https://help.github.com/articles/fork-a-repo)
|
||||
* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request)
|
||||
|
||||
### Patch review
|
||||
|
||||
Help review existing open pull requests by commenting on the code or
|
||||
Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or
|
||||
proposed functionality.
|
||||
|
||||
### Bug reports
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2013 - 2014
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the {organization} nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -76,17 +76,19 @@ Or
|
|||
|
||||
# Cases
|
||||
|
||||
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
|
||||
|
||||
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
|
||||
|
||||
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
|
||||
|
||||
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
|
||||
|
||||
* [Very Hour](http://veryhour.com/)
|
||||
|
||||
# Todo
|
||||
|
||||
[Todo List](https://trello.com/b/IHsuAnhk/xorm)
|
||||
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
|
||||
|
||||
# Discuss
|
||||
|
||||
|
|
|
@ -79,17 +79,19 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
|
||||
## 案例
|
||||
|
||||
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
|
||||
|
||||
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
|
||||
|
||||
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
|
||||
|
||||
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
|
||||
|
||||
* [Very Hour](http://veryhour.com/)
|
||||
|
||||
## Todo
|
||||
|
||||
[开发计划](https://trello.com/b/IHsuAnhk/xorm)
|
||||
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
|
||||
|
||||
## 讨论
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type LRUCacher struct {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
go test -v -bench=. -run=XXX
|
|
@ -1,77 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// default cache expired time
|
||||
CacheExpired = 60 * time.Minute
|
||||
// not use now
|
||||
CacheMaxMemory = 256
|
||||
// evey ten minutes to clear all expired nodes
|
||||
CacheGcInterval = 10 * time.Minute
|
||||
// each time when gc to removed max nodes
|
||||
CacheGcMaxRemoved = 20
|
||||
)
|
||||
|
||||
// CacheStore is a interface to store cache
|
||||
type CacheStore interface {
|
||||
// key is primary key or composite primary key or unique key's value
|
||||
// value is struct's pointer
|
||||
// key format : <tablename>-p-<pk1>-<pk2>...
|
||||
Put(key string, value interface{}) error
|
||||
Get(key string) (interface{}, error)
|
||||
Del(key string) error
|
||||
}
|
||||
|
||||
// Cacher is an interface to provide cache
|
||||
// id format : u-<pk1>-<pk2>...
|
||||
type Cacher interface {
|
||||
GetIds(tableName, sql string) interface{}
|
||||
GetBean(tableName string, id string) interface{}
|
||||
PutIds(tableName, sql string, ids interface{})
|
||||
PutBean(tableName string, id string, obj interface{})
|
||||
DelIds(tableName, sql string)
|
||||
DelBean(tableName string, id string)
|
||||
ClearIds(tableName string)
|
||||
ClearBeans(tableName string)
|
||||
}
|
||||
|
||||
func encodeIds(ids []PK) (string, error) {
|
||||
b, err := json.Marshal(ids)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func decodeIds(s string) ([]PK, error) {
|
||||
pks := make([]PK, 0)
|
||||
err := json.Unmarshal([]byte(s), &pks)
|
||||
return pks, err
|
||||
}
|
||||
|
||||
func GetCacheSql(m Cacher, tableName, sql string, args interface{}) ([]PK, error) {
|
||||
bytes := m.GetIds(tableName, GenSqlKey(sql, args))
|
||||
if bytes == nil {
|
||||
return nil, errors.New("Not Exist")
|
||||
}
|
||||
return decodeIds(bytes.(string))
|
||||
}
|
||||
|
||||
func PutCacheSql(m Cacher, ids []PK, tableName, sql string, args interface{}) error {
|
||||
bytes, err := encodeIds(ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.PutIds(tableName, GenSqlKey(sql, args), bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenSqlKey(sql string, args interface{}) string {
|
||||
return fmt.Sprintf("%v-%v", sql, args)
|
||||
}
|
113
core/column.go
113
core/column.go
|
@ -1,113 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
TWOSIDES = iota + 1
|
||||
ONLYTODB
|
||||
ONLYFROMDB
|
||||
)
|
||||
|
||||
// database column
|
||||
type Column struct {
|
||||
Name string
|
||||
FieldName string
|
||||
SQLType SQLType
|
||||
Length int
|
||||
Length2 int
|
||||
Nullable bool
|
||||
Default string
|
||||
Indexes map[string]bool
|
||||
IsPrimaryKey bool
|
||||
IsAutoIncrement bool
|
||||
MapType int
|
||||
IsCreated bool
|
||||
IsUpdated bool
|
||||
IsCascade bool
|
||||
IsVersion bool
|
||||
fieldPath []string
|
||||
}
|
||||
|
||||
func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
|
||||
return &Column{name, fieldName, sqlType, len1, len2, nullable, "", make(map[string]bool), false, false,
|
||||
TWOSIDES, false, false, false, false, nil}
|
||||
}
|
||||
|
||||
// generate column description string according dialect
|
||||
func (col *Column) String(d Dialect) string {
|
||||
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||
|
||||
sql += d.SqlType(col) + " "
|
||||
|
||||
if col.IsPrimaryKey {
|
||||
sql += "PRIMARY KEY "
|
||||
if col.IsAutoIncrement {
|
||||
sql += d.AutoIncrStr() + " "
|
||||
}
|
||||
}
|
||||
|
||||
if col.Nullable {
|
||||
sql += "NULL "
|
||||
} else {
|
||||
sql += "NOT NULL "
|
||||
}
|
||||
|
||||
if col.Default != "" {
|
||||
sql += "DEFAULT " + col.Default + " "
|
||||
}
|
||||
|
||||
return sql
|
||||
}
|
||||
|
||||
func (col *Column) StringNoPk(d Dialect) string {
|
||||
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||
|
||||
sql += d.SqlType(col) + " "
|
||||
|
||||
if col.Nullable {
|
||||
sql += "NULL "
|
||||
} else {
|
||||
sql += "NOT NULL "
|
||||
}
|
||||
|
||||
if col.Default != "" {
|
||||
sql += "DEFAULT " + col.Default + " "
|
||||
}
|
||||
|
||||
return sql
|
||||
}
|
||||
|
||||
// return col's filed of struct's value
|
||||
func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) {
|
||||
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
|
||||
return col.ValueOfV(&dataStruct)
|
||||
}
|
||||
|
||||
func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
|
||||
var fieldValue reflect.Value
|
||||
var err error
|
||||
if col.fieldPath == nil {
|
||||
col.fieldPath = strings.Split(col.FieldName, ".")
|
||||
}
|
||||
|
||||
if len(col.fieldPath) == 1 {
|
||||
fieldValue = dataStruct.FieldByName(col.FieldName)
|
||||
} else if len(col.fieldPath) == 2 {
|
||||
parentField := dataStruct.FieldByName(col.fieldPath[0])
|
||||
if parentField.IsValid() {
|
||||
fieldValue = parentField.FieldByName(col.fieldPath[1])
|
||||
} else {
|
||||
err = fmt.Errorf("field %v is not valid", col.FieldName)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Unsupported mutliderive %v", col.FieldName)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fieldValue, nil
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package core
|
||||
|
||||
// Conversion is an interface. A type implements Conversion will according
|
||||
// the custom method to fill into database and retrieve from database.
|
||||
type Conversion interface {
|
||||
FromDB([]byte) error
|
||||
ToDB() ([]byte, error)
|
||||
}
|
566
core/db.go
566
core/db.go
|
@ -1,566 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoMapPointer = errors.New("mp should be a map's pointer")
|
||||
ErrNoStructPointer = errors.New("mp should be a map's pointer")
|
||||
)
|
||||
|
||||
func MapToSlice(query string, mp interface{}) (string, []interface{}, error) {
|
||||
vv := reflect.ValueOf(mp)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return "", []interface{}{}, ErrNoMapPointer
|
||||
}
|
||||
|
||||
args := make([]interface{}, 0)
|
||||
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||
args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface())
|
||||
return "?"
|
||||
})
|
||||
return query, args, nil
|
||||
}
|
||||
|
||||
func StructToSlice(query string, st interface{}) (string, []interface{}, error) {
|
||||
vv := reflect.ValueOf(st)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return "", []interface{}{}, ErrNoStructPointer
|
||||
}
|
||||
|
||||
args := make([]interface{}, 0)
|
||||
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||
args = append(args, vv.Elem().FieldByName(src[1:]).Interface())
|
||||
return "?"
|
||||
})
|
||||
return query, args, nil
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
*sql.DB
|
||||
Mapper IMapper
|
||||
}
|
||||
|
||||
func Open(driverName, dataSourceName string) (*DB, error) {
|
||||
db, err := sql.Open(driverName, dataSourceName)
|
||||
return &DB{db, NewCacheMapper(&SnakeMapper{})}, err
|
||||
}
|
||||
|
||||
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
|
||||
rows, err := db.DB.Query(query, args...)
|
||||
return &Rows{rows, db.Mapper}, err
|
||||
}
|
||||
|
||||
func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.Query(query, args...)
|
||||
}
|
||||
|
||||
func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.Query(query, args...)
|
||||
}
|
||||
|
||||
type Row struct {
|
||||
*sql.Row
|
||||
// One of these two will be non-nil:
|
||||
err error // deferred error for easy chaining
|
||||
Mapper IMapper
|
||||
}
|
||||
|
||||
func (row *Row) Scan(dest ...interface{}) error {
|
||||
if row.err != nil {
|
||||
return row.err
|
||||
}
|
||||
return row.Row.Scan(dest...)
|
||||
}
|
||||
|
||||
func (db *DB) QueryRow(query string, args ...interface{}) *Row {
|
||||
row := db.DB.QueryRow(query, args...)
|
||||
return &Row{row, nil, db.Mapper}
|
||||
}
|
||||
|
||||
func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return &Row{nil, err, db.Mapper}
|
||||
}
|
||||
return db.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return &Row{nil, err, db.Mapper}
|
||||
}
|
||||
return db.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
type Stmt struct {
|
||||
*sql.Stmt
|
||||
Mapper IMapper
|
||||
names map[string]int
|
||||
}
|
||||
|
||||
func (db *DB) Prepare(query string) (*Stmt, error) {
|
||||
names := make(map[string]int)
|
||||
var i int
|
||||
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||
names[src[1:]] = i
|
||||
i += 1
|
||||
return "?"
|
||||
})
|
||||
|
||||
stmt, err := db.DB.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Stmt{stmt, db.Mapper, names}, nil
|
||||
}
|
||||
|
||||
func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) {
|
||||
vv := reflect.ValueOf(mp)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return nil, errors.New("mp should be a map's pointer")
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||
}
|
||||
return s.Stmt.Exec(args...)
|
||||
}
|
||||
|
||||
func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) {
|
||||
vv := reflect.ValueOf(st)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return nil, errors.New("mp should be a map's pointer")
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||
}
|
||||
return s.Stmt.Exec(args...)
|
||||
}
|
||||
|
||||
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
|
||||
rows, err := s.Stmt.Query(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Rows{rows, s.Mapper}, nil
|
||||
}
|
||||
|
||||
func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) {
|
||||
vv := reflect.ValueOf(mp)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return nil, errors.New("mp should be a map's pointer")
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||
}
|
||||
|
||||
return s.Query(args...)
|
||||
}
|
||||
|
||||
func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
|
||||
vv := reflect.ValueOf(st)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return nil, errors.New("mp should be a map's pointer")
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||
}
|
||||
|
||||
return s.Query(args...)
|
||||
}
|
||||
|
||||
func (s *Stmt) QueryRow(args ...interface{}) *Row {
|
||||
row := s.Stmt.QueryRow(args...)
|
||||
return &Row{row, nil, s.Mapper}
|
||||
}
|
||||
|
||||
func (s *Stmt) QueryRowMap(mp interface{}) *Row {
|
||||
vv := reflect.ValueOf(mp)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return &Row{nil, errors.New("mp should be a map's pointer"), s.Mapper}
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||
}
|
||||
|
||||
return s.QueryRow(args...)
|
||||
}
|
||||
|
||||
func (s *Stmt) QueryRowStruct(st interface{}) *Row {
|
||||
vv := reflect.ValueOf(st)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return &Row{nil, errors.New("st should be a struct's pointer"), s.Mapper}
|
||||
}
|
||||
|
||||
args := make([]interface{}, len(s.names))
|
||||
for k, i := range s.names {
|
||||
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||
}
|
||||
|
||||
return s.QueryRow(args...)
|
||||
}
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile(`[?](\w+)`)
|
||||
)
|
||||
|
||||
// insert into (name) values (?)
|
||||
// insert into (name) values (?name)
|
||||
func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.DB.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.DB.Exec(query, args...)
|
||||
}
|
||||
|
||||
type Rows struct {
|
||||
*sql.Rows
|
||||
Mapper IMapper
|
||||
}
|
||||
|
||||
// scan data to a struct's pointer according field index
|
||||
func (rs *Rows) ScanStruct(dest ...interface{}) error {
|
||||
if len(dest) == 0 {
|
||||
return errors.New("at least one struct")
|
||||
}
|
||||
|
||||
vvvs := make([]reflect.Value, len(dest))
|
||||
for i, s := range dest {
|
||||
vv := reflect.ValueOf(s)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return errors.New("dest should be a struct's pointer")
|
||||
}
|
||||
|
||||
vvvs[i] = vv.Elem()
|
||||
}
|
||||
|
||||
cols, err := rs.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newDest := make([]interface{}, len(cols))
|
||||
|
||||
var i = 0
|
||||
for _, vvv := range vvvs {
|
||||
for j := 0; j < vvv.NumField(); j++ {
|
||||
newDest[i] = vvv.Field(j).Addr().Interface()
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
func fieldByName(v reflect.Value, name string) reflect.Value {
|
||||
t := v.Type()
|
||||
fieldCacheMutex.RLock()
|
||||
cache, ok := fieldCache[t]
|
||||
fieldCacheMutex.RUnlock()
|
||||
if !ok {
|
||||
cache = make(map[string]int)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
cache[t.Field(i).Name] = i
|
||||
}
|
||||
fieldCacheMutex.Lock()
|
||||
fieldCache[t] = cache
|
||||
fieldCacheMutex.Unlock()
|
||||
}
|
||||
|
||||
if i, ok := cache[name]; ok {
|
||||
return v.Field(i)
|
||||
}
|
||||
|
||||
return reflect.Zero(t)
|
||||
}
|
||||
|
||||
// scan data to a struct's pointer according field name
|
||||
func (rs *Rows) ScanStruct2(dest interface{}) error {
|
||||
vv := reflect.ValueOf(dest)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||
return errors.New("dest should be a struct's pointer")
|
||||
}
|
||||
|
||||
cols, err := rs.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newDest := make([]interface{}, len(cols))
|
||||
var v EmptyScanner
|
||||
for j, name := range cols {
|
||||
f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name))
|
||||
if f.IsValid() {
|
||||
newDest[j] = f.Addr().Interface()
|
||||
} else {
|
||||
newDest[j] = &v
|
||||
}
|
||||
}
|
||||
|
||||
return rs.Rows.Scan(newDest...)
|
||||
}
|
||||
|
||||
type cacheStruct struct {
|
||||
value reflect.Value
|
||||
idx int
|
||||
}
|
||||
|
||||
var (
|
||||
reflectCache = make(map[reflect.Type]*cacheStruct)
|
||||
reflectCacheMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func ReflectNew(typ reflect.Type) reflect.Value {
|
||||
reflectCacheMutex.RLock()
|
||||
cs, ok := reflectCache[typ]
|
||||
reflectCacheMutex.RUnlock()
|
||||
|
||||
const newSize = 200
|
||||
|
||||
if !ok || cs.idx+1 > newSize-1 {
|
||||
cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0}
|
||||
reflectCacheMutex.Lock()
|
||||
reflectCache[typ] = cs
|
||||
reflectCacheMutex.Unlock()
|
||||
} else {
|
||||
reflectCacheMutex.Lock()
|
||||
cs.idx = cs.idx + 1
|
||||
reflectCacheMutex.Unlock()
|
||||
}
|
||||
return cs.value.Index(cs.idx).Addr()
|
||||
}
|
||||
|
||||
// scan data to a slice's pointer, slice's length should equal to columns' number
|
||||
func (rs *Rows) ScanSlice(dest interface{}) error {
|
||||
vv := reflect.ValueOf(dest)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Slice {
|
||||
return errors.New("dest should be a slice's pointer")
|
||||
}
|
||||
|
||||
vvv := vv.Elem()
|
||||
cols, err := rs.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newDest := make([]interface{}, len(cols))
|
||||
|
||||
for j := 0; j < len(cols); j++ {
|
||||
if j >= vvv.Len() {
|
||||
newDest[j] = reflect.New(vvv.Type().Elem()).Interface()
|
||||
} else {
|
||||
newDest[j] = vvv.Index(j).Addr().Interface()
|
||||
}
|
||||
}
|
||||
|
||||
err = rs.Rows.Scan(newDest...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcLen := vvv.Len()
|
||||
for i := srcLen; i < len(cols); i++ {
|
||||
vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// scan data to a map's pointer
|
||||
func (rs *Rows) ScanMap(dest interface{}) error {
|
||||
vv := reflect.ValueOf(dest)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return errors.New("dest should be a map's pointer")
|
||||
}
|
||||
|
||||
cols, err := rs.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newDest := make([]interface{}, len(cols))
|
||||
vvv := vv.Elem()
|
||||
|
||||
for i, _ := range cols {
|
||||
newDest[i] = ReflectNew(vvv.Type().Elem()).Interface()
|
||||
//v := reflect.New(vvv.Type().Elem())
|
||||
//newDest[i] = v.Interface()
|
||||
}
|
||||
|
||||
err = rs.Rows.Scan(newDest...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, name := range cols {
|
||||
vname := reflect.ValueOf(name)
|
||||
vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*func (rs *Rows) ScanMap(dest interface{}) error {
|
||||
vv := reflect.ValueOf(dest)
|
||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||
return errors.New("dest should be a map's pointer")
|
||||
}
|
||||
|
||||
cols, err := rs.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newDest := make([]interface{}, len(cols))
|
||||
err = rs.ScanSlice(newDest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vvv := vv.Elem()
|
||||
|
||||
for i, name := range cols {
|
||||
vname := reflect.ValueOf(name)
|
||||
vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem())
|
||||
}
|
||||
|
||||
return nil
|
||||
}*/
|
||||
|
||||
type Tx struct {
|
||||
*sql.Tx
|
||||
Mapper IMapper
|
||||
}
|
||||
|
||||
func (db *DB) Begin() (*Tx, error) {
|
||||
tx, err := db.DB.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Tx{tx, db.Mapper}, nil
|
||||
}
|
||||
|
||||
func (tx *Tx) Prepare(query string) (*Stmt, error) {
|
||||
names := make(map[string]int)
|
||||
var i int
|
||||
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||
names[src[1:]] = i
|
||||
i += 1
|
||||
return "?"
|
||||
})
|
||||
|
||||
stmt, err := tx.Tx.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Stmt{stmt, tx.Mapper, names}, nil
|
||||
}
|
||||
|
||||
func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
|
||||
// TODO:
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.Tx.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.Tx.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
|
||||
rows, err := tx.Tx.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Rows{rows, tx.Mapper}, nil
|
||||
}
|
||||
|
||||
func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.Query(query, args...)
|
||||
}
|
||||
|
||||
func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.Query(query, args...)
|
||||
}
|
||||
|
||||
func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
|
||||
row := tx.Tx.QueryRow(query, args...)
|
||||
return &Row{row, nil, tx.Mapper}
|
||||
}
|
||||
|
||||
func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
|
||||
query, args, err := MapToSlice(query, mp)
|
||||
if err != nil {
|
||||
return &Row{nil, err, tx.Mapper}
|
||||
}
|
||||
return tx.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row {
|
||||
query, args, err := StructToSlice(query, st)
|
||||
if err != nil {
|
||||
return &Row{nil, err, tx.Mapper}
|
||||
}
|
||||
return tx.QueryRow(query, args...)
|
||||
}
|
634
core/db_test.go
634
core/db_test.go
|
@ -1,634 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var (
|
||||
createTableSqlite3 = "CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, " +
|
||||
"`title` TEXT NULL, `age` FLOAT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL, `created` datetime);"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Name string
|
||||
Title string
|
||||
Age float32
|
||||
Alias string
|
||||
NickName string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
func BenchmarkOriQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var Id int64
|
||||
var Name, Title, Alias, NickName string
|
||||
var Age float32
|
||||
var Created time.Time
|
||||
err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
//fmt.Println(Id, Name, Title, Age, Alias, NickName)
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStructQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var user User
|
||||
err = rows.ScanStruct(&user)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if user.Name != "xlw" {
|
||||
fmt.Println(user)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStruct2Query(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
db.Mapper = NewCacheMapper(&SnakeMapper{})
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var user User
|
||||
err = rows.ScanStruct2(&user)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if user.Name != "xlw" {
|
||||
fmt.Println(user)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSliceInterfaceQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
slice := make([]interface{}, len(cols))
|
||||
err = rows.ScanSlice(&slice)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if slice[1].(string) != "xlw" {
|
||||
fmt.Println(slice)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
/*func BenchmarkSliceBytesQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
slice := make([][]byte, len(cols))
|
||||
err = rows.ScanSlice(&slice)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if string(slice[1]) != "xlw" {
|
||||
fmt.Println(slice)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSliceStringQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
slice := make([]string, len(cols))
|
||||
err = rows.ScanSlice(&slice)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if slice[1] != "xlw" {
|
||||
fmt.Println(slice)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}*/
|
||||
|
||||
func BenchmarkMapInterfaceQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
m := make(map[string]interface{})
|
||||
err = rows.ScanMap(&m)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if m["name"].(string) != "xlw" {
|
||||
fmt.Println(m)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
/*func BenchmarkMapBytesQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
m := make(map[string][]byte)
|
||||
err = rows.ScanMap(&m)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if string(m["name"]) != "xlw" {
|
||||
fmt.Println(m)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMapStringQuery(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
m := make(map[string]string)
|
||||
err = rows.ScanMap(&m)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if m["name"] != "xlw" {
|
||||
fmt.Println(m)
|
||||
b.Error(errors.New("name should be xlw"))
|
||||
}
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
}*/
|
||||
|
||||
func BenchmarkExec(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExecMap(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
mp := map[string]interface{}{
|
||||
"name": "xlw",
|
||||
"title": "tester",
|
||||
"age": 1.2,
|
||||
"alias": "lunny",
|
||||
"nick_name": "lunny xiao",
|
||||
"created": time.Now(),
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name, created)
|
||||
values (?name,?title,?age,?alias,?nick_name,?created)`,
|
||||
&mp)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecMap(t *testing.T) {
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mp := map[string]interface{}{
|
||||
"name": "xlw",
|
||||
"title": "tester",
|
||||
"age": 1.2,
|
||||
"alias": "lunny",
|
||||
"nick_name": "lunny xiao",
|
||||
"created": time.Now(),
|
||||
}
|
||||
|
||||
_, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name,created)
|
||||
values (?name,?title,?age,?alias,?nick_name,?created)`,
|
||||
&mp)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select * from user")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var user User
|
||||
err = rows.ScanStruct2(&user)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println("--", user)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecStruct(t *testing.T) {
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
user := User{Name: "xlw",
|
||||
Title: "tester",
|
||||
Age: 1.2,
|
||||
Alias: "lunny",
|
||||
NickName: "lunny xiao",
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
_, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name,created)
|
||||
values (?Name,?Title,?Age,?Alias,?NickName,?Created)`,
|
||||
&user)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
rows, err := db.QueryStruct("select * from user where name = ?Name", &user)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var user User
|
||||
err = rows.ScanStruct2(&user)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println("1--", user)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExecStruct(b *testing.B) {
|
||||
b.StopTimer()
|
||||
os.Remove("./test.db")
|
||||
db, err := Open("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSqlite3)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
user := User{Name: "xlw",
|
||||
Title: "tester",
|
||||
Age: 1.2,
|
||||
Alias: "lunny",
|
||||
NickName: "lunny xiao",
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name,created)
|
||||
values (?Name,?Title,?Age,?Alias,?NickName,?Created)`,
|
||||
&user)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
144
core/dialect.go
144
core/dialect.go
|
@ -1,144 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dbType string
|
||||
|
||||
type Uri struct {
|
||||
DbType dbType
|
||||
Proto string
|
||||
Host string
|
||||
Port string
|
||||
DbName string
|
||||
User string
|
||||
Passwd string
|
||||
Charset string
|
||||
Laddr string
|
||||
Raddr string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// a dialect is a driver's wrapper
|
||||
type Dialect interface {
|
||||
Init(*Uri, string, string) error
|
||||
URI() *Uri
|
||||
DBType() dbType
|
||||
SqlType(t *Column) string
|
||||
SupportInsertMany() bool
|
||||
QuoteStr() string
|
||||
AutoIncrStr() string
|
||||
SupportEngine() bool
|
||||
SupportCharset() bool
|
||||
IndexOnTable() bool
|
||||
|
||||
IndexCheckSql(tableName, idxName string) (string, []interface{})
|
||||
TableCheckSql(tableName string) (string, []interface{})
|
||||
ColumnCheckSql(tableName, colName string) (string, []interface{})
|
||||
|
||||
GetColumns(tableName string) ([]string, map[string]*Column, error)
|
||||
GetTables() ([]*Table, error)
|
||||
GetIndexes(tableName string) (map[string]*Index, error)
|
||||
|
||||
CreateTableSql(table *Table, tableName, storeEngine, charset string) string
|
||||
Filters() []Filter
|
||||
|
||||
DriverName() string
|
||||
DataSourceName() string
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
dialect Dialect
|
||||
driverName string
|
||||
dataSourceName string
|
||||
*Uri
|
||||
}
|
||||
|
||||
func (b *Base) Init(dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
|
||||
b.dialect = dialect
|
||||
b.driverName, b.dataSourceName = drivername, dataSourceName
|
||||
b.Uri = uri
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Base) URI() *Uri {
|
||||
return b.Uri
|
||||
}
|
||||
|
||||
func (b *Base) DBType() dbType {
|
||||
return b.Uri.DbType
|
||||
}
|
||||
|
||||
func (b *Base) DriverName() string {
|
||||
return b.driverName
|
||||
}
|
||||
|
||||
func (b *Base) DataSourceName() string {
|
||||
return b.dataSourceName
|
||||
}
|
||||
|
||||
func (b *Base) Quote(c string) string {
|
||||
return b.dialect.QuoteStr() + c + b.dialect.QuoteStr()
|
||||
}
|
||||
|
||||
func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
|
||||
var sql string
|
||||
sql = "CREATE TABLE IF NOT EXISTS "
|
||||
if tableName == "" {
|
||||
tableName = table.Name
|
||||
}
|
||||
|
||||
sql += b.Quote(tableName) + " ("
|
||||
|
||||
pkList := table.PrimaryKeys
|
||||
|
||||
for _, colName := range table.ColumnsSeq() {
|
||||
col := table.GetColumn(colName)
|
||||
if col.IsPrimaryKey && len(pkList) == 1 {
|
||||
sql += col.String(b.dialect)
|
||||
} else {
|
||||
sql += col.StringNoPk(b.dialect)
|
||||
}
|
||||
sql = strings.TrimSpace(sql)
|
||||
sql += ", "
|
||||
}
|
||||
|
||||
if len(pkList) > 1 {
|
||||
sql += "PRIMARY KEY ( "
|
||||
sql += b.Quote(strings.Join(pkList, b.Quote(",")))
|
||||
sql += " ), "
|
||||
}
|
||||
|
||||
sql = sql[:len(sql)-2] + ")"
|
||||
if b.dialect.SupportEngine() && storeEngine != "" {
|
||||
sql += " ENGINE=" + storeEngine
|
||||
}
|
||||
if b.dialect.SupportCharset() {
|
||||
if charset == "" {
|
||||
charset = b.dialect.URI().Charset
|
||||
}
|
||||
sql += " DEFAULT CHARSET " + charset
|
||||
}
|
||||
sql += ";"
|
||||
return sql
|
||||
}
|
||||
|
||||
var (
|
||||
dialects = map[dbType]Dialect{}
|
||||
)
|
||||
|
||||
func RegisterDialect(dbName dbType, dialect Dialect) {
|
||||
if dialect == nil {
|
||||
panic("core: Register dialect is nil")
|
||||
}
|
||||
if _, dup := dialects[dbName]; dup {
|
||||
panic("core: Register called twice for dialect " + dbName)
|
||||
}
|
||||
dialects[dbName] = dialect
|
||||
}
|
||||
|
||||
func QueryDialect(dbName dbType) Dialect {
|
||||
return dialects[dbName]
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package core
|
||||
|
||||
type driver interface {
|
||||
Parse(string, string) (*Uri, error)
|
||||
}
|
||||
|
||||
var (
|
||||
drivers = map[string]driver{}
|
||||
)
|
||||
|
||||
func RegisterDriver(driverName string, driver driver) {
|
||||
if driver == nil {
|
||||
panic("core: Register driver is nil")
|
||||
}
|
||||
if _, dup := drivers[driverName]; dup {
|
||||
panic("core: Register called twice for driver " + driverName)
|
||||
}
|
||||
drivers[driverName] = driver
|
||||
}
|
||||
|
||||
func QueryDriver(driverName string) driver {
|
||||
return drivers[driverName]
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package core
|
||||
|
||||
import "strings"
|
||||
|
||||
// Filter is an interface to filter SQL
|
||||
type Filter interface {
|
||||
Do(sql string, dialect Dialect, table *Table) string
|
||||
}
|
||||
|
||||
// QuoteFilter filter SQL replace ` to database's own quote character
|
||||
type QuoteFilter struct {
|
||||
}
|
||||
|
||||
func (s *QuoteFilter) Do(sql string, dialect Dialect, table *Table) string {
|
||||
return strings.Replace(sql, "`", dialect.QuoteStr(), -1)
|
||||
}
|
||||
|
||||
// IdFilter filter SQL replace (id) to primary key column name
|
||||
type IdFilter struct {
|
||||
}
|
||||
|
||||
type Quoter struct {
|
||||
dialect Dialect
|
||||
}
|
||||
|
||||
func NewQuoter(dialect Dialect) *Quoter {
|
||||
return &Quoter{dialect}
|
||||
}
|
||||
|
||||
func (q *Quoter) Quote(content string) string {
|
||||
return q.dialect.QuoteStr() + content + q.dialect.QuoteStr()
|
||||
}
|
||||
|
||||
func (i *IdFilter) Do(sql string, dialect Dialect, table *Table) string {
|
||||
quoter := NewQuoter(dialect)
|
||||
if table != nil && len(table.PrimaryKeys) == 1 {
|
||||
sql = strings.Replace(sql, "`(id)`", quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||
sql = strings.Replace(sql, quoter.Quote("(id)"), quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||
return strings.Replace(sql, "(id)", quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||
}
|
||||
return sql
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package core
|
||||
|
||||
const (
|
||||
IndexType = iota + 1
|
||||
UniqueType
|
||||
)
|
||||
|
||||
// database index
|
||||
type Index struct {
|
||||
Name string
|
||||
Type int
|
||||
Cols []string
|
||||
}
|
||||
|
||||
// add columns which will be composite index
|
||||
func (index *Index) AddColumn(cols ...string) {
|
||||
for _, col := range cols {
|
||||
index.Cols = append(index.Cols, col)
|
||||
}
|
||||
}
|
||||
|
||||
// new an index
|
||||
func NewIndex(name string, indexType int) *Index {
|
||||
return &Index{name, indexType, make([]string, 0)}
|
||||
}
|
176
core/mapper.go
176
core/mapper.go
|
@ -1,176 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// name translation between struct, fields names and table, column names
|
||||
type IMapper interface {
|
||||
Obj2Table(string) string
|
||||
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 {
|
||||
}
|
||||
|
||||
func (m SameMapper) Obj2Table(o string) string {
|
||||
return o
|
||||
}
|
||||
|
||||
func (m SameMapper) Table2Obj(t string) string {
|
||||
return t
|
||||
}
|
||||
|
||||
// SnakeMapper implements IMapper and provides name transaltion between
|
||||
// struct and database table
|
||||
type SnakeMapper struct {
|
||||
}
|
||||
|
||||
func snakeCasedName(name string) string {
|
||||
newstr := make([]rune, 0)
|
||||
for idx, chr := range name {
|
||||
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
|
||||
if idx > 0 {
|
||||
newstr = append(newstr, '_')
|
||||
}
|
||||
chr -= ('A' - 'a')
|
||||
}
|
||||
newstr = append(newstr, chr)
|
||||
}
|
||||
|
||||
return string(newstr)
|
||||
}
|
||||
|
||||
/*func pascal2Sql(s string) (d string) {
|
||||
d = ""
|
||||
lastIdx := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= 'A' && s[i] <= 'Z' {
|
||||
if lastIdx < i {
|
||||
d += s[lastIdx+1 : i]
|
||||
}
|
||||
if i != 0 {
|
||||
d += "_"
|
||||
}
|
||||
d += string(s[i] + 32)
|
||||
lastIdx = i
|
||||
}
|
||||
}
|
||||
d += s[lastIdx+1:]
|
||||
return
|
||||
}*/
|
||||
|
||||
func (mapper SnakeMapper) Obj2Table(name string) string {
|
||||
return snakeCasedName(name)
|
||||
}
|
||||
|
||||
func titleCasedName(name string) string {
|
||||
newstr := make([]rune, 0)
|
||||
upNextChar := true
|
||||
|
||||
name = strings.ToLower(name)
|
||||
|
||||
for _, chr := range name {
|
||||
switch {
|
||||
case upNextChar:
|
||||
upNextChar = false
|
||||
if 'a' <= chr && chr <= 'z' {
|
||||
chr -= ('a' - 'A')
|
||||
}
|
||||
case chr == '_':
|
||||
upNextChar = true
|
||||
continue
|
||||
}
|
||||
|
||||
newstr = append(newstr, chr)
|
||||
}
|
||||
|
||||
return string(newstr)
|
||||
}
|
||||
|
||||
func (mapper SnakeMapper) Table2Obj(name string) string {
|
||||
return titleCasedName(name)
|
||||
}
|
||||
|
||||
// provide prefix table name support
|
||||
type PrefixMapper struct {
|
||||
Mapper IMapper
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func (mapper PrefixMapper) Obj2Table(name string) string {
|
||||
return mapper.Prefix + mapper.Mapper.Obj2Table(name)
|
||||
}
|
||||
|
||||
func (mapper PrefixMapper) Table2Obj(name string) string {
|
||||
return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):])
|
||||
}
|
||||
|
||||
func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper {
|
||||
return PrefixMapper{mapper, prefix}
|
||||
}
|
||||
|
||||
// provide suffix table name support
|
||||
type SuffixMapper struct {
|
||||
Mapper IMapper
|
||||
Suffix string
|
||||
}
|
||||
|
||||
func (mapper SuffixMapper) Obj2Table(name string) string {
|
||||
return mapper.Suffix + mapper.Mapper.Obj2Table(name)
|
||||
}
|
||||
|
||||
func (mapper SuffixMapper) Table2Obj(name string) string {
|
||||
return mapper.Mapper.Table2Obj(name[len(mapper.Suffix):])
|
||||
}
|
||||
|
||||
func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper {
|
||||
return SuffixMapper{mapper, suffix}
|
||||
}
|
25
core/pk.go
25
core/pk.go
|
@ -1,25 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type PK []interface{}
|
||||
|
||||
func NewPK(pks ...interface{}) *PK {
|
||||
p := PK(pks)
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *PK) ToString() (string, error) {
|
||||
bs, err := json.Marshal(*p)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return string(bs), nil
|
||||
}
|
||||
|
||||
func (p *PK) FromString(content string) error {
|
||||
return json.Unmarshal([]byte(content), p)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPK(t *testing.T) {
|
||||
p := NewPK(1, 3, "string")
|
||||
str, err := p.ToString()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(str)
|
||||
|
||||
s := &PK{}
|
||||
err = s.FromString(str)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// database table
|
||||
type Table struct {
|
||||
Name string
|
||||
Type reflect.Type
|
||||
columnsSeq []string
|
||||
columns map[string]*Column
|
||||
Indexes map[string]*Index
|
||||
PrimaryKeys []string
|
||||
AutoIncrement string
|
||||
Created map[string]bool
|
||||
Updated string
|
||||
Version string
|
||||
}
|
||||
|
||||
func (table *Table) Columns() map[string]*Column {
|
||||
return table.columns
|
||||
}
|
||||
|
||||
func (table *Table) ColumnsSeq() []string {
|
||||
return table.columnsSeq
|
||||
}
|
||||
|
||||
func NewEmptyTable() *Table {
|
||||
return &Table{columnsSeq: make([]string, 0),
|
||||
columns: make(map[string]*Column),
|
||||
Indexes: make(map[string]*Index),
|
||||
Created: make(map[string]bool),
|
||||
PrimaryKeys: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTable(name string, t reflect.Type) *Table {
|
||||
return &Table{Name: name, Type: t,
|
||||
columnsSeq: make([]string, 0),
|
||||
columns: make(map[string]*Column),
|
||||
Indexes: make(map[string]*Index),
|
||||
Created: make(map[string]bool),
|
||||
PrimaryKeys: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (table *Table) GetColumn(name string) *Column {
|
||||
return table.columns[strings.ToLower(name)]
|
||||
}
|
||||
|
||||
// if has primary key, return column
|
||||
func (table *Table) PKColumns() []*Column {
|
||||
columns := make([]*Column, 0)
|
||||
for _, name := range table.PrimaryKeys {
|
||||
columns = append(columns, table.GetColumn(name))
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
func (table *Table) AutoIncrColumn() *Column {
|
||||
return table.GetColumn(table.AutoIncrement)
|
||||
}
|
||||
|
||||
func (table *Table) VersionColumn() *Column {
|
||||
return table.GetColumn(table.Version)
|
||||
}
|
||||
|
||||
// add a column to table
|
||||
func (table *Table) AddColumn(col *Column) {
|
||||
table.columnsSeq = append(table.columnsSeq, col.Name)
|
||||
table.columns[strings.ToLower(col.Name)] = col
|
||||
if col.IsPrimaryKey {
|
||||
table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
|
||||
}
|
||||
if col.IsAutoIncrement {
|
||||
table.AutoIncrement = col.Name
|
||||
}
|
||||
if col.IsCreated {
|
||||
table.Created[col.Name] = true
|
||||
}
|
||||
if col.IsUpdated {
|
||||
table.Updated = col.Name
|
||||
}
|
||||
if col.IsVersion {
|
||||
table.Version = col.Name
|
||||
}
|
||||
}
|
||||
|
||||
// add an index or an unique to table
|
||||
func (table *Table) AddIndex(index *Index) {
|
||||
table.Indexes[index.Name] = index
|
||||
}
|
288
core/type.go
288
core/type.go
|
@ -1,288 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
POSTGRES = "postgres"
|
||||
SQLITE = "sqlite3"
|
||||
MYSQL = "mysql"
|
||||
MSSQL = "mssql"
|
||||
ORACLE = "oracle"
|
||||
)
|
||||
|
||||
// xorm SQL types
|
||||
type SQLType struct {
|
||||
Name string
|
||||
DefaultLength int
|
||||
DefaultLength2 int
|
||||
}
|
||||
|
||||
func (s *SQLType) IsText() bool {
|
||||
return s.Name == Char || s.Name == Varchar || s.Name == TinyText ||
|
||||
s.Name == Text || s.Name == MediumText || s.Name == LongText
|
||||
}
|
||||
|
||||
func (s *SQLType) IsBlob() bool {
|
||||
return (s.Name == TinyBlob) || (s.Name == Blob) ||
|
||||
s.Name == MediumBlob || s.Name == LongBlob ||
|
||||
s.Name == Binary || s.Name == VarBinary || s.Name == Bytea
|
||||
}
|
||||
|
||||
var (
|
||||
Bit = "BIT"
|
||||
TinyInt = "TINYINT"
|
||||
SmallInt = "SMALLINT"
|
||||
MediumInt = "MEDIUMINT"
|
||||
Int = "INT"
|
||||
Integer = "INTEGER"
|
||||
BigInt = "BIGINT"
|
||||
|
||||
Char = "CHAR"
|
||||
Varchar = "VARCHAR"
|
||||
TinyText = "TINYTEXT"
|
||||
Text = "TEXT"
|
||||
MediumText = "MEDIUMTEXT"
|
||||
LongText = "LONGTEXT"
|
||||
|
||||
Date = "DATE"
|
||||
DateTime = "DATETIME"
|
||||
Time = "TIME"
|
||||
TimeStamp = "TIMESTAMP"
|
||||
TimeStampz = "TIMESTAMPZ"
|
||||
|
||||
Decimal = "DECIMAL"
|
||||
Numeric = "NUMERIC"
|
||||
|
||||
Real = "REAL"
|
||||
Float = "FLOAT"
|
||||
Double = "DOUBLE"
|
||||
|
||||
Binary = "BINARY"
|
||||
VarBinary = "VARBINARY"
|
||||
TinyBlob = "TINYBLOB"
|
||||
Blob = "BLOB"
|
||||
MediumBlob = "MEDIUMBLOB"
|
||||
LongBlob = "LONGBLOB"
|
||||
Bytea = "BYTEA"
|
||||
|
||||
Bool = "BOOL"
|
||||
|
||||
Serial = "SERIAL"
|
||||
BigSerial = "BIGSERIAL"
|
||||
|
||||
SqlTypes = map[string]bool{
|
||||
Bit: true,
|
||||
TinyInt: true,
|
||||
SmallInt: true,
|
||||
MediumInt: true,
|
||||
Int: true,
|
||||
Integer: true,
|
||||
BigInt: true,
|
||||
|
||||
Char: true,
|
||||
Varchar: true,
|
||||
TinyText: true,
|
||||
Text: true,
|
||||
MediumText: true,
|
||||
LongText: true,
|
||||
|
||||
Date: true,
|
||||
DateTime: true,
|
||||
Time: true,
|
||||
TimeStamp: true,
|
||||
TimeStampz: true,
|
||||
|
||||
Decimal: true,
|
||||
Numeric: true,
|
||||
|
||||
Binary: true,
|
||||
VarBinary: true,
|
||||
Real: true,
|
||||
Float: true,
|
||||
Double: true,
|
||||
TinyBlob: true,
|
||||
Blob: true,
|
||||
MediumBlob: true,
|
||||
LongBlob: true,
|
||||
Bytea: true,
|
||||
|
||||
Bool: true,
|
||||
|
||||
Serial: true,
|
||||
BigSerial: true,
|
||||
}
|
||||
|
||||
intTypes = sort.StringSlice{"*int", "*int16", "*int32", "*int8"}
|
||||
uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"}
|
||||
)
|
||||
|
||||
// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparision
|
||||
var (
|
||||
c_EMPTY_STRING string
|
||||
c_BOOL_DEFAULT bool
|
||||
c_BYTE_DEFAULT byte
|
||||
c_COMPLEX64_DEFAULT complex64
|
||||
c_COMPLEX128_DEFAULT complex128
|
||||
c_FLOAT32_DEFAULT float32
|
||||
c_FLOAT64_DEFAULT float64
|
||||
c_INT64_DEFAULT int64
|
||||
c_UINT64_DEFAULT uint64
|
||||
c_INT32_DEFAULT int32
|
||||
c_UINT32_DEFAULT uint32
|
||||
c_INT16_DEFAULT int16
|
||||
c_UINT16_DEFAULT uint16
|
||||
c_INT8_DEFAULT int8
|
||||
c_UINT8_DEFAULT uint8
|
||||
c_INT_DEFAULT int
|
||||
c_UINT_DEFAULT uint
|
||||
c_TIME_DEFAULT time.Time
|
||||
)
|
||||
|
||||
var (
|
||||
IntType = reflect.TypeOf(c_INT_DEFAULT)
|
||||
Int8Type = reflect.TypeOf(c_INT8_DEFAULT)
|
||||
Int16Type = reflect.TypeOf(c_INT16_DEFAULT)
|
||||
Int32Type = reflect.TypeOf(c_INT32_DEFAULT)
|
||||
Int64Type = reflect.TypeOf(c_INT64_DEFAULT)
|
||||
|
||||
UintType = reflect.TypeOf(c_UINT_DEFAULT)
|
||||
Uint8Type = reflect.TypeOf(c_UINT8_DEFAULT)
|
||||
Uint16Type = reflect.TypeOf(c_UINT16_DEFAULT)
|
||||
Uint32Type = reflect.TypeOf(c_UINT32_DEFAULT)
|
||||
Uint64Type = reflect.TypeOf(c_UINT64_DEFAULT)
|
||||
|
||||
Float32Type = reflect.TypeOf(c_FLOAT32_DEFAULT)
|
||||
Float64Type = reflect.TypeOf(c_FLOAT64_DEFAULT)
|
||||
|
||||
Complex64Type = reflect.TypeOf(c_COMPLEX64_DEFAULT)
|
||||
Complex128Type = reflect.TypeOf(c_COMPLEX128_DEFAULT)
|
||||
|
||||
StringType = reflect.TypeOf(c_EMPTY_STRING)
|
||||
BoolType = reflect.TypeOf(c_BOOL_DEFAULT)
|
||||
ByteType = reflect.TypeOf(c_BYTE_DEFAULT)
|
||||
|
||||
TimeType = reflect.TypeOf(c_TIME_DEFAULT)
|
||||
)
|
||||
|
||||
var (
|
||||
PtrIntType = reflect.PtrTo(IntType)
|
||||
PtrInt8Type = reflect.PtrTo(Int8Type)
|
||||
PtrInt16Type = reflect.PtrTo(Int16Type)
|
||||
PtrInt32Type = reflect.PtrTo(Int32Type)
|
||||
PtrInt64Type = reflect.PtrTo(Int64Type)
|
||||
|
||||
PtrUintType = reflect.PtrTo(UintType)
|
||||
PtrUint8Type = reflect.PtrTo(Uint8Type)
|
||||
PtrUint16Type = reflect.PtrTo(Uint16Type)
|
||||
PtrUint32Type = reflect.PtrTo(Uint32Type)
|
||||
PtrUint64Type = reflect.PtrTo(Uint64Type)
|
||||
|
||||
PtrFloat32Type = reflect.PtrTo(Float32Type)
|
||||
PtrFloat64Type = reflect.PtrTo(Float64Type)
|
||||
|
||||
PtrComplex64Type = reflect.PtrTo(Complex64Type)
|
||||
PtrComplex128Type = reflect.PtrTo(Complex128Type)
|
||||
|
||||
PtrStringType = reflect.PtrTo(StringType)
|
||||
PtrBoolType = reflect.PtrTo(BoolType)
|
||||
PtrByteType = reflect.PtrTo(ByteType)
|
||||
|
||||
PtrTimeType = reflect.PtrTo(TimeType)
|
||||
)
|
||||
|
||||
func Type2SQLType(t reflect.Type) (st SQLType) {
|
||||
|
||||
switch k := t.Kind(); k {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
st = SQLType{Int, 0, 0}
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
st = SQLType{BigInt, 0, 0}
|
||||
case reflect.Float32:
|
||||
st = SQLType{Float, 0, 0}
|
||||
case reflect.Float64:
|
||||
st = SQLType{Double, 0, 0}
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
st = SQLType{Varchar, 64, 0}
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
if t.Elem() == reflect.TypeOf(c_BYTE_DEFAULT) {
|
||||
st = SQLType{Blob, 0, 0}
|
||||
} else {
|
||||
st = SQLType{Text, 0, 0}
|
||||
}
|
||||
case reflect.Bool:
|
||||
st = SQLType{Bool, 0, 0}
|
||||
case reflect.String:
|
||||
st = SQLType{Varchar, 255, 0}
|
||||
case reflect.Struct:
|
||||
if t == reflect.TypeOf(c_TIME_DEFAULT) {
|
||||
st = SQLType{DateTime, 0, 0}
|
||||
} else {
|
||||
// TODO need to handle association struct
|
||||
st = SQLType{Text, 0, 0}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
st, _ = ptrType2SQLType(t)
|
||||
default:
|
||||
st = SQLType{Text, 0, 0}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ptrType2SQLType(t reflect.Type) (st SQLType, has bool) {
|
||||
has = true
|
||||
|
||||
switch t {
|
||||
case reflect.TypeOf(&c_EMPTY_STRING):
|
||||
st = SQLType{Varchar, 255, 0}
|
||||
return
|
||||
case reflect.TypeOf(&c_BOOL_DEFAULT):
|
||||
st = SQLType{Bool, 0, 0}
|
||||
case reflect.TypeOf(&c_COMPLEX64_DEFAULT), reflect.TypeOf(&c_COMPLEX128_DEFAULT):
|
||||
st = SQLType{Varchar, 64, 0}
|
||||
case reflect.TypeOf(&c_FLOAT32_DEFAULT):
|
||||
st = SQLType{Float, 0, 0}
|
||||
case reflect.TypeOf(&c_FLOAT64_DEFAULT):
|
||||
st = SQLType{Double, 0, 0}
|
||||
case reflect.TypeOf(&c_INT64_DEFAULT), reflect.TypeOf(&c_UINT64_DEFAULT):
|
||||
st = SQLType{BigInt, 0, 0}
|
||||
case reflect.TypeOf(&c_TIME_DEFAULT):
|
||||
st = SQLType{DateTime, 0, 0}
|
||||
case reflect.TypeOf(&c_INT_DEFAULT), reflect.TypeOf(&c_INT32_DEFAULT), reflect.TypeOf(&c_INT8_DEFAULT), reflect.TypeOf(&c_INT16_DEFAULT), reflect.TypeOf(&c_UINT_DEFAULT), reflect.TypeOf(&c_UINT32_DEFAULT), reflect.TypeOf(&c_UINT8_DEFAULT), reflect.TypeOf(&c_UINT16_DEFAULT):
|
||||
st = SQLType{Int, 0, 0}
|
||||
default:
|
||||
has = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// default sql type change to go types
|
||||
func SQLType2Type(st SQLType) reflect.Type {
|
||||
name := strings.ToUpper(st.Name)
|
||||
switch name {
|
||||
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, Serial:
|
||||
return reflect.TypeOf(1)
|
||||
case BigInt, BigSerial:
|
||||
return reflect.TypeOf(int64(1))
|
||||
case Float, Real:
|
||||
return reflect.TypeOf(float32(1))
|
||||
case Double:
|
||||
return reflect.TypeOf(float64(1))
|
||||
case Char, Varchar, TinyText, Text, MediumText, LongText:
|
||||
return reflect.TypeOf("")
|
||||
case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary:
|
||||
return reflect.TypeOf([]byte{})
|
||||
case Bool:
|
||||
return reflect.TypeOf(true)
|
||||
case DateTime, Date, Time, TimeStamp, TimeStampz:
|
||||
return reflect.TypeOf(c_TIME_DEFAULT)
|
||||
case Decimal, Numeric:
|
||||
return reflect.TypeOf("")
|
||||
default:
|
||||
return reflect.TypeOf("")
|
||||
}
|
||||
}
|
|
@ -1,16 +1,12 @@
|
|||
package dialects
|
||||
|
||||
import (
|
||||
//"crypto/tls"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
//"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
//"time"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
. "github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -90,6 +86,12 @@ func (db *mssql) AutoIncrStr() string {
|
|||
return "IDENTITY"
|
||||
}
|
||||
|
||||
func (db *mssql) DropTableSql(tableName string) string {
|
||||
return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+
|
||||
"object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+
|
||||
"DROP TABLE \"%s\"", tableName, tableName)
|
||||
}
|
||||
|
||||
func (db *mssql) SupportCharset() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -166,6 +168,10 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
. "github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -37,6 +37,7 @@ func (db *mysql) SqlType(c *Column) string {
|
|||
switch t := c.SQLType.Name; t {
|
||||
case Bool:
|
||||
res = TinyInt
|
||||
c.Length = 1
|
||||
case Serial:
|
||||
c.IsAutoIncrement = true
|
||||
c.IsPrimaryKey = true
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
. "github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -150,6 +150,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
. "github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -127,6 +127,7 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
for rows.Next() {
|
||||
col := new(Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
|
||||
var colName, isNullable, dataType string
|
||||
var maxLenStr, colDefault, numPrecision, numRadix *string
|
||||
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix)
|
||||
|
@ -183,6 +184,10 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
|
@ -3,7 +3,7 @@ package dialects
|
|||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
. "github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -4,7 +4,7 @@ xorm 快速入门
|
|||
* [1.创建Orm引擎](#10)
|
||||
* [2.定义表结构体](#20)
|
||||
* [2.1.名称映射规则](#21)
|
||||
* [2.2.前缀映射规则和后缀映射规则](#22)
|
||||
* [2.2.前缀映射,后缀映射和缓存映射](#22)
|
||||
* [2.3.使用Table和Tag改变名称映射](#23)
|
||||
* [2.4.Column属性定义](#24)
|
||||
* [2.5.Go与字段类型对应表](#25)
|
||||
|
@ -29,12 +29,13 @@ xorm 快速入门
|
|||
* [9.执行SQL命令](#100)
|
||||
* [10.事务处理](#110)
|
||||
* [11.缓存](#120)
|
||||
* [12.xorm工具](#130)
|
||||
* [12.1.反转命令](#131)
|
||||
* [13.Examples](#140)
|
||||
* [14.案例](#150)
|
||||
* [15.那些年我们踩过的坑](#160)
|
||||
* [16.讨论](#170)
|
||||
* [12.事件](#125)
|
||||
* [13.xorm工具](#130)
|
||||
* [13.1.反转命令](#131)
|
||||
* [14.Examples](#140)
|
||||
* [15.案例](#150)
|
||||
* [16.那些年我们踩过的坑](#160)
|
||||
* [17.讨论](#170)
|
||||
|
||||
<a name="10" id="10"></a>
|
||||
## 1.创建Orm引擎
|
||||
|
@ -64,8 +65,11 @@ defer engine.Close()
|
|||
一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
|
||||
|
||||
xorm当前支持五种驱动四个数据库如下:
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
|
||||
>>>>>>> master
|
||||
|
||||
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
|
||||
|
||||
|
@ -120,27 +124,39 @@ engine.SetMapper(SameMapper{})
|
|||
|
||||
同时需要注意的是:
|
||||
|
||||
<<<<<<< HEAD
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
=======
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
>>>>>>> master
|
||||
```Go
|
||||
engine.SetTableMapper(SameMapper{})
|
||||
engine.SetColumnMapper(SnakeMapper{})
|
||||
```
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.前缀映射规则和后缀映射规则
|
||||
### 2.2.前缀映射,后缀映射和缓存映射
|
||||
|
||||
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
<<<<<<< HEAD
|
||||
*
|
||||
=======
|
||||
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。
|
||||
>>>>>>> master
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.3.使用Table和Tag改变名称映射
|
||||
|
||||
如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。
|
||||
|
||||
通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
* 如果struct拥有`Tablename() string`的成员方法,那么此方法的返回值即是该struct默认对应的数据库表名。
|
||||
|
||||
* 通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.4.Column属性定义
|
||||
|
@ -153,7 +169,7 @@ type User struct {
|
|||
}
|
||||
```
|
||||
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。
|
||||
|
||||
具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写:
|
||||
|
||||
|
@ -407,7 +423,11 @@ engine.Cols("age", "name").Update(&user)
|
|||
// UPDATE user SET age=? AND name=?
|
||||
```
|
||||
|
||||
其中的参数"age", "name"也可以写成"age, name",两种写法均可
|
||||
* AllCols()
|
||||
查询或更新所有字段。
|
||||
|
||||
* MustCols(…string)
|
||||
某些字段必须更新。
|
||||
|
||||
* Omit(...string)
|
||||
和cols相反,此函数指定排除某些指定的字段。注意:此方法和Cols方法不可同时使用
|
||||
|
@ -578,13 +598,21 @@ affected, err := engine.Id(id).Update(user)
|
|||
|
||||
这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择:
|
||||
|
||||
<<<<<<< HEAD
|
||||
1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。
|
||||
=======
|
||||
* 1.通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。
|
||||
>>>>>>> master
|
||||
|
||||
```Go
|
||||
affected, err := engine.Id(id).Cols("age").Update(&user)
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。
|
||||
=======
|
||||
* 2.通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。
|
||||
>>>>>>> master
|
||||
|
||||
```Go
|
||||
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
|
||||
|
@ -681,6 +709,8 @@ if err != nil {
|
|||
}
|
||||
```
|
||||
|
||||
* 注意如果您使用的是mysql,数据库引擎为innodb事务才有效,myisam引擎是不支持事务的。
|
||||
|
||||
<a name="120" id="120"></a>
|
||||
## 11.缓存
|
||||
|
||||
|
@ -727,20 +757,46 @@ engine.ClearCache(new(User))
|
|||
|
||||

|
||||
|
||||
<a name="125" id="125"></a>
|
||||
## 12.事件
|
||||
xorm支持两种方式的事件,一种是在Struct中的特定方法来作为事件的方法,一种是在执行语句的过程中执行事件。
|
||||
|
||||
在Struct中作为成员方法的事件如下:
|
||||
|
||||
* BeforeInsert()
|
||||
|
||||
* BeforeUpdate()
|
||||
|
||||
* BeforeDelete()
|
||||
|
||||
* AfterInsert()
|
||||
|
||||
* AfterUpdate()
|
||||
|
||||
* AfterDelete()
|
||||
|
||||
在语句执行过程中的事件方法为:
|
||||
|
||||
* Before(beforeFunc interface{})
|
||||
|
||||
* After(afterFunc interface{})
|
||||
|
||||
其中beforeFunc和afterFunc的原型为func(bean interface{}).
|
||||
|
||||
<a name="130" id="130"></a>
|
||||
## 12.xorm工具
|
||||
## 13.xorm工具
|
||||
xorm工具提供了xorm命令,能够帮助做很多事情。
|
||||
|
||||
### 12.1.反转命令
|
||||
### 13.1.反转命令
|
||||
参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm)
|
||||
|
||||
<a name="140" id="140"></a>
|
||||
## 13.Examples
|
||||
## 14.Examples
|
||||
|
||||
请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples)
|
||||
|
||||
<a name="150" id="150"></a>
|
||||
## 14.案例
|
||||
## 15.案例
|
||||
|
||||
* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
|
@ -751,7 +807,7 @@ xorm工具提供了xorm命令,能够帮助做很多事情。
|
|||
* [VeryHour](http://veryhour.com)
|
||||
|
||||
<a name="160" id="160"></a>
|
||||
## 15.那些年我们踩过的坑
|
||||
## 16.那些年我们踩过的坑
|
||||
* 怎么同时使用xorm的tag和json的tag?
|
||||
|
||||
答:使用空格
|
||||
|
@ -795,5 +851,5 @@ money float64 `xorm:"Numeric"`
|
|||
|
||||
|
||||
<a name="170" id="170"></a>
|
||||
## 16.讨论
|
||||
## 17.讨论
|
||||
请加入QQ群:280360085 进行讨论。
|
||||
|
|
|
@ -100,30 +100,47 @@ engine.Logger = f
|
|||
<a name="20" id="20"></a>
|
||||
## 2.Define struct
|
||||
|
||||
xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下:
|
||||
xorm map a struct to a database table, the rule is below.
|
||||
|
||||
<a name="21" id="21"></a>
|
||||
### 2.1.名称映射规则
|
||||
### 2.1.name mapping rule
|
||||
|
||||
名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持相同的命名。
|
||||
use xorm.IMapper interface to implement. There are two IMapper implemented: `SnakeMapper` and `SameMapper`. SnakeMapper means struct name is word by word and table name or column name as 下划线. SameMapper means same name between struct and table.
|
||||
|
||||
当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用
|
||||
SnakeMapper is the default.
|
||||
|
||||
```Go
|
||||
engine.Mapper = SameMapper{}
|
||||
```
|
||||
|
||||
同时需要注意的是:
|
||||
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
```Go
|
||||
engine.SetTableMapper(SameMapper{})
|
||||
engine.SetColumnMapper(SnakeMapper{})
|
||||
```
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.前缀映射规则,后缀映射规则和缓存映射规则
|
||||
|
||||
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以起到在内存中缓存曾经映射过的命名映射。
|
||||
|
||||
当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.使用Table和Tag改变名称映射
|
||||
### 2.3.使用Table和Tag改变名称映射
|
||||
|
||||
如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。
|
||||
|
||||
通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.3.Column属性定义
|
||||
### 2.4.Column属性定义
|
||||
我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如:
|
||||
|
||||
```
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package drivers
|
||||
|
||||
import (
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
207
engine.go
207
engine.go
|
@ -12,7 +12,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Engine is the major struct of xorm, it means a database manager.
|
||||
|
@ -35,7 +35,6 @@ type Engine struct {
|
|||
Filters []core.Filter
|
||||
Logger ILogger // io.Writer
|
||||
Cacher core.Cacher
|
||||
tableCachers map[reflect.Type]core.Cacher
|
||||
}
|
||||
|
||||
func (engine *Engine) SetMapper(mapper core.IMapper) {
|
||||
|
@ -117,9 +116,9 @@ func (engine *Engine) NoCascade() *Session {
|
|||
|
||||
// Set a table use a special cacher
|
||||
func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
|
||||
t := rType(bean)
|
||||
engine.autoMapType(t)
|
||||
engine.tableCachers[t] = cacher
|
||||
v := rValue(bean)
|
||||
engine.autoMapType(v)
|
||||
engine.Tables[v.Type()].Cacher = cacher
|
||||
}
|
||||
|
||||
// OpenDB provides a interface to operate database directly.
|
||||
|
@ -308,6 +307,18 @@ func (engine *Engine) Cols(columns ...string) *Session {
|
|||
return session.Cols(columns...)
|
||||
}
|
||||
|
||||
func (engine *Engine) AllCols() *Session {
|
||||
session := engine.NewSession()
|
||||
session.IsAutoClose = true
|
||||
return session.AllCols()
|
||||
}
|
||||
|
||||
func (engine *Engine) MustCols(columns ...string) *Session {
|
||||
session := engine.NewSession()
|
||||
session.IsAutoClose = true
|
||||
return session.MustCols(columns...)
|
||||
}
|
||||
|
||||
// Xorm automatically retrieve condition according struct, but
|
||||
// if struct has bool field, it will ignore them. So use UseBool
|
||||
// to tell system to do not ignore them.
|
||||
|
@ -395,12 +406,13 @@ func (engine *Engine) Having(conditions string) *Session {
|
|||
return session.Having(conditions)
|
||||
}
|
||||
|
||||
func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
|
||||
func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
|
||||
t := v.Type()
|
||||
engine.mutex.RLock()
|
||||
table, ok := engine.Tables[t]
|
||||
engine.mutex.RUnlock()
|
||||
if !ok {
|
||||
table = engine.mapType(t)
|
||||
table = engine.mapType(v)
|
||||
engine.mutex.Lock()
|
||||
engine.Tables[t] = table
|
||||
engine.mutex.Unlock()
|
||||
|
@ -409,26 +421,66 @@ func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
|
|||
}
|
||||
|
||||
func (engine *Engine) autoMap(bean interface{}) *core.Table {
|
||||
t := rType(bean)
|
||||
return engine.autoMapType(t)
|
||||
v := rValue(bean)
|
||||
return engine.autoMapType(v)
|
||||
}
|
||||
|
||||
func (engine *Engine) mapType(t reflect.Type) *core.Table {
|
||||
/*func (engine *Engine) mapType(t reflect.Type) *core.Table {
|
||||
return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
|
||||
table := core.NewEmptyTable()
|
||||
table.Name = tableMapper.Obj2Table(t.Name())
|
||||
*/
|
||||
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, indexType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) newTable() *core.Table {
|
||||
table := core.NewEmptyTable()
|
||||
table.Cacher = engine.Cacher
|
||||
return table
|
||||
}
|
||||
|
||||
func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
||||
t := v.Type()
|
||||
table := engine.newTable()
|
||||
method := v.MethodByName("TableName")
|
||||
if !method.IsValid() {
|
||||
method = v.Addr().MethodByName("TableName")
|
||||
}
|
||||
if method.IsValid() {
|
||||
params := []reflect.Value{}
|
||||
results := method.Call(params)
|
||||
if len(results) == 1 {
|
||||
table.Name = results[0].Interface().(string)
|
||||
}
|
||||
}
|
||||
|
||||
if table.Name == "" {
|
||||
table.Name = engine.TableMapper.Obj2Table(t.Name())
|
||||
}
|
||||
table.Type = t
|
||||
|
||||
var idFieldColName string
|
||||
var err error
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
tag := t.Field(i).Tag
|
||||
ormTagStr := tag.Get(tagId)
|
||||
ormTagStr := tag.Get(engine.TagIdentifier)
|
||||
var col *core.Column
|
||||
fieldType := t.Field(i).Type
|
||||
fieldValue := v.Field(i)
|
||||
fieldType := fieldValue.Type()
|
||||
|
||||
if ormTagStr != "" {
|
||||
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
|
||||
|
@ -441,7 +493,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
}
|
||||
if (strings.ToUpper(tags[0]) == "EXTENDS") &&
|
||||
(fieldType.Kind() == reflect.Struct) {
|
||||
parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
|
||||
|
||||
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
|
||||
parentTable := engine.mapType(fieldValue)
|
||||
for _, col := range parentTable.Columns() {
|
||||
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
|
||||
table.AddColumn(col)
|
||||
|
@ -450,8 +504,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
continue
|
||||
}
|
||||
|
||||
var indexType int
|
||||
var indexName string
|
||||
indexNames := make(map[string]int)
|
||||
var isIndex, isUnique bool
|
||||
var preKey string
|
||||
for j, key := range tags {
|
||||
k := strings.ToUpper(key)
|
||||
switch {
|
||||
|
@ -464,8 +519,18 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
col.Nullable = false
|
||||
case k == "NULL":
|
||||
col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT")
|
||||
/*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
|
||||
col.IsAutoIncrement = true
|
||||
|
||||
autoStart := k[len("AUTOINCR")+1 : len(k)-1]
|
||||
autoStartInt, err := strconv.Atoi(autoStart)
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
col.AutoIncrStart = autoStartInt*/
|
||||
case k == "AUTOINCR":
|
||||
col.IsAutoIncrement = true
|
||||
//col.AutoIncrStart = 1
|
||||
case k == "DEFAULT":
|
||||
col.Default = tags[j+1]
|
||||
case k == "CREATED":
|
||||
|
@ -476,35 +541,46 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
case k == "UPDATED":
|
||||
col.IsUpdated = true
|
||||
case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"):
|
||||
indexType = core.IndexType
|
||||
indexName = k[len("INDEX")+1 : len(k)-1]
|
||||
indexName := k[len("INDEX")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.IndexType
|
||||
case k == "INDEX":
|
||||
indexType = core.IndexType
|
||||
isIndex = true
|
||||
case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
|
||||
indexName = k[len("UNIQUE")+1 : len(k)-1]
|
||||
indexType = core.UniqueType
|
||||
indexName := k[len("UNIQUE")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.UniqueType
|
||||
case k == "UNIQUE":
|
||||
indexType = core.UniqueType
|
||||
isUnique = true
|
||||
case k == "NOTNULL":
|
||||
col.Nullable = false
|
||||
case k == "NOT":
|
||||
default:
|
||||
if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
|
||||
if key != col.Default {
|
||||
if preKey != "DEFAULT" {
|
||||
col.Name = key[1 : len(key)-1]
|
||||
}
|
||||
} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
|
||||
fs := strings.Split(k, "(")
|
||||
|
||||
if _, ok := core.SqlTypes[fs[0]]; !ok {
|
||||
preKey = k
|
||||
continue
|
||||
}
|
||||
col.SQLType = core.SQLType{fs[0], 0, 0}
|
||||
fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
|
||||
if len(fs2) == 2 {
|
||||
col.Length, _ = strconv.Atoi(fs2[0])
|
||||
col.Length2, _ = strconv.Atoi(fs2[1])
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
col.Length2, err = strconv.Atoi(fs2[1])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
} else if len(fs2) == 1 {
|
||||
col.Length, _ = strconv.Atoi(fs2[0])
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := core.SqlTypes[k]; ok {
|
||||
|
@ -513,8 +589,10 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
col.Name = key
|
||||
}
|
||||
}
|
||||
dialect.SqlType(col)
|
||||
panic("broken")
|
||||
//engine.dialect.SqlType(col)
|
||||
}
|
||||
preKey = k
|
||||
}
|
||||
if col.SQLType.Name == "" {
|
||||
col.SQLType = core.Type2SQLType(fieldType)
|
||||
|
@ -527,40 +605,24 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
}
|
||||
//fmt.Println("======", col)
|
||||
if col.Name == "" {
|
||||
col.Name = colMapper.Obj2Table(t.Field(i).Name)
|
||||
col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
|
||||
}
|
||||
if indexType == core.IndexType {
|
||||
if indexName == "" {
|
||||
indexName = col.Name
|
||||
}
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, core.IndexType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
}
|
||||
} else if indexType == core.UniqueType {
|
||||
if indexName == "" {
|
||||
indexName = col.Name
|
||||
}
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, core.UniqueType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
|
||||
if isUnique {
|
||||
indexNames[col.Name] = core.UniqueType
|
||||
} else if isIndex {
|
||||
indexNames[col.Name] = core.IndexType
|
||||
}
|
||||
|
||||
for indexName, indexType := range indexNames {
|
||||
addIndex(indexName, table, col, indexType)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sqlType := core.Type2SQLType(fieldType)
|
||||
col = core.NewColumn(colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType,
|
||||
sqlType.DefaultLength, sqlType.DefaultLength2, true)
|
||||
col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
|
||||
t.Field(i).Name, sqlType, sqlType.DefaultLength,
|
||||
sqlType.DefaultLength2, true)
|
||||
}
|
||||
if col.IsAutoIncrement {
|
||||
col.Nullable = false
|
||||
|
@ -590,19 +652,20 @@ func (engine *Engine) mapping(beans ...interface{}) (e error) {
|
|||
engine.mutex.Lock()
|
||||
defer engine.mutex.Unlock()
|
||||
for _, bean := range beans {
|
||||
t := rType(bean)
|
||||
engine.Tables[t] = engine.mapType(t)
|
||||
v := rValue(bean)
|
||||
engine.Tables[v.Type()] = engine.mapType(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If a table has any reocrd
|
||||
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
||||
t := rType(bean)
|
||||
v := rValue(bean)
|
||||
t := v.Type()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return false, errors.New("bean should be a struct or struct's point")
|
||||
}
|
||||
engine.autoMapType(t)
|
||||
engine.autoMapType(v)
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
rows, err := session.Count(bean)
|
||||
|
@ -611,11 +674,11 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
|||
|
||||
// If a table is exist
|
||||
func (engine *Engine) IsTableExist(bean interface{}) (bool, error) {
|
||||
t := rType(bean)
|
||||
if t.Kind() != reflect.Struct {
|
||||
v := rValue(bean)
|
||||
if v.Type().Kind() != reflect.Struct {
|
||||
return false, errors.New("bean should be a struct or struct's point")
|
||||
}
|
||||
table := engine.autoMapType(t)
|
||||
table := engine.autoMapType(v)
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
has, err := session.isTableExist(table.Name)
|
||||
|
@ -654,9 +717,13 @@ func (engine *Engine) CreateUniques(bean interface{}) error {
|
|||
return session.CreateUniques(bean)
|
||||
}
|
||||
|
||||
func (engine *Engine) getCacher(t reflect.Type) core.Cacher {
|
||||
if cacher, ok := engine.tableCachers[t]; ok {
|
||||
return cacher
|
||||
func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
|
||||
return table.Cacher
|
||||
}
|
||||
|
||||
func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
|
||||
if table := engine.autoMapType(v); table != nil {
|
||||
return table.Cacher
|
||||
}
|
||||
return engine.Cacher
|
||||
}
|
||||
|
@ -668,7 +735,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
table := engine.autoMap(bean)
|
||||
cacher := engine.getCacher(t)
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
}
|
||||
if cacher != nil {
|
||||
cacher.ClearIds(table.Name)
|
||||
cacher.DelBean(table.Name, id)
|
||||
|
@ -684,7 +754,10 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
table := engine.autoMap(bean)
|
||||
cacher := engine.getCacher(t)
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
}
|
||||
if cacher != nil {
|
||||
cacher.ClearIds(table.Name)
|
||||
cacher.ClearBeans(table.Name)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func indexNoCase(s, sep string) int {
|
||||
|
@ -38,9 +38,14 @@ func makeArray(elem string, count int) []string {
|
|||
return res
|
||||
}
|
||||
|
||||
func rValue(bean interface{}) reflect.Value {
|
||||
return reflect.Indirect(reflect.ValueOf(bean))
|
||||
}
|
||||
|
||||
func rType(bean interface{}) reflect.Type {
|
||||
sliceValue := reflect.Indirect(reflect.ValueOf(bean))
|
||||
return reflect.TypeOf(sliceValue.Interface())
|
||||
//return reflect.TypeOf(sliceValue.Interface())
|
||||
return sliceValue.Type()
|
||||
}
|
||||
|
||||
func structName(v reflect.Type) string {
|
||||
|
|
7
pool.go
7
pool.go
|
@ -1,13 +1,12 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"github.com/lunny/xorm/core"
|
||||
//"fmt"
|
||||
"sync"
|
||||
//"sync/atomic"
|
||||
"container/list"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Interface IConnecPool is a connection pool interface, all implements should implement
|
||||
|
|
2
rows.go
2
rows.go
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type Rows struct {
|
||||
|
|
79
session.go
79
session.go
|
@ -11,7 +11,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Struct Session keep a pointer to sql.DB and provides all execution of all
|
||||
|
@ -134,6 +134,16 @@ func (session *Session) Cols(columns ...string) *Session {
|
|||
return session
|
||||
}
|
||||
|
||||
func (session *Session) AllCols() *Session {
|
||||
session.Statement.AllCols()
|
||||
return session
|
||||
}
|
||||
|
||||
func (session *Session) MustCols(columns ...string) *Session {
|
||||
session.Statement.MustCols(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
func (session *Session) NoCascade() *Session {
|
||||
session.Statement.UseCascade = false
|
||||
return session
|
||||
|
@ -290,7 +300,7 @@ func (session *Session) Begin() error {
|
|||
// When using transaction, you can rollback if any error
|
||||
func (session *Session) Rollback() error {
|
||||
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
|
||||
session.Engine.logSQL("ROLL BACK")
|
||||
session.Engine.logSQL(session.Engine.dialect.RollBackStr())
|
||||
session.IsCommitedOrRollbacked = true
|
||||
return session.Tx.Rollback()
|
||||
}
|
||||
|
@ -357,13 +367,14 @@ func cleanupProcessorsClosures(slices *[]func(interface{})) {
|
|||
}
|
||||
|
||||
func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
|
||||
dataStruct := reflect.Indirect(reflect.ValueOf(obj))
|
||||
dataStruct := rValue(obj)
|
||||
if dataStruct.Kind() != reflect.Struct {
|
||||
return errors.New("Expected a pointer to a struct")
|
||||
}
|
||||
|
||||
table := session.Engine.autoMapType(rType(obj))
|
||||
var col *core.Column
|
||||
table := session.Engine.autoMapType(dataStruct)
|
||||
|
||||
for key, data := range objMap {
|
||||
if col = table.GetColumn(key); col == nil {
|
||||
session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.Columns()))
|
||||
|
@ -609,7 +620,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
|
|||
return false, ErrCacheFailed
|
||||
}
|
||||
|
||||
cacher := session.Engine.getCacher(session.Statement.RefTable.Type)
|
||||
cacher := session.Engine.getCacher2(session.Statement.RefTable)
|
||||
tableName := session.Statement.TableName()
|
||||
session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args)
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
|
||||
|
@ -698,7 +709,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
}
|
||||
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(t)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args)
|
||||
if err != nil {
|
||||
//session.Engine.LogError(err)
|
||||
|
@ -927,6 +938,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
session.Statement.Limit(1)
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
|
||||
session.Statement.RefTable = session.Engine.autoMap(bean)
|
||||
|
||||
if session.Statement.RawSQL == "" {
|
||||
|
@ -936,7 +948,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
args = session.Statement.RawParams
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
|
||||
has, err := session.cacheGet(bean, sqlStr, args...)
|
||||
if err != ErrCacheFailed {
|
||||
return has, err
|
||||
|
@ -1051,12 +1063,14 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
if session.Statement.RefTable == nil {
|
||||
if sliceElementType.Kind() == reflect.Ptr {
|
||||
if sliceElementType.Elem().Kind() == reflect.Struct {
|
||||
table = session.Engine.autoMapType(sliceElementType.Elem())
|
||||
pv := reflect.New(sliceElementType.Elem())
|
||||
table = session.Engine.autoMapType(pv.Elem())
|
||||
} else {
|
||||
return errors.New("slice type")
|
||||
}
|
||||
} else if sliceElementType.Kind() == reflect.Struct {
|
||||
table = session.Engine.autoMapType(sliceElementType)
|
||||
pv := reflect.New(sliceElementType)
|
||||
table = session.Engine.autoMapType(pv.Elem())
|
||||
} else {
|
||||
return errors.New("slice type")
|
||||
}
|
||||
|
@ -1067,7 +1081,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
|
||||
if len(condiBean) > 0 {
|
||||
colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
session.Statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
session.Statement.BeanArgs = args
|
||||
}
|
||||
|
@ -1089,7 +1104,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
args = session.Statement.RawParams
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil &&
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil &&
|
||||
session.Statement.UseCache &&
|
||||
!session.Statement.IsDistinct {
|
||||
err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
|
||||
|
@ -1426,13 +1441,12 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
|
|||
}
|
||||
|
||||
func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
|
||||
|
||||
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
|
||||
dataStruct := rValue(bean)
|
||||
if dataStruct.Kind() != reflect.Struct {
|
||||
return errors.New("Expected a pointer to a struct")
|
||||
}
|
||||
|
||||
table := session.Engine.autoMapType(rType(bean))
|
||||
table := session.Engine.autoMapType(dataStruct)
|
||||
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
|
@ -1533,7 +1547,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
|
|||
fieldValue.Set(vv)
|
||||
}
|
||||
} else if session.Statement.UseCascade {
|
||||
table := session.Engine.autoMapType(fieldValue.Type())
|
||||
table := session.Engine.autoMapType(*fieldValue)
|
||||
if table != nil {
|
||||
var x int64
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
|
@ -1801,9 +1815,10 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
}
|
||||
|
||||
bean := sliceValue.Index(0).Interface()
|
||||
sliceElementType := rType(bean)
|
||||
elementValue := rValue(bean)
|
||||
//sliceElementType := elementValue.Type()
|
||||
|
||||
table := session.Engine.autoMapType(sliceElementType)
|
||||
table := session.Engine.autoMapType(elementValue)
|
||||
session.Statement.RefTable = table
|
||||
|
||||
size := sliceValue.Len()
|
||||
|
@ -1901,7 +1916,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
return 0, err
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
@ -2111,7 +2126,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
|
|||
v = x
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
} else if session.Statement.UseCascade {
|
||||
table := session.Engine.autoMapType(fieldValue.Type())
|
||||
table := session.Engine.autoMapType(*fieldValue)
|
||||
if table != nil {
|
||||
x, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
|
@ -2524,6 +2539,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
}
|
||||
|
||||
colPlaces := strings.Repeat("?, ", len(colNames))
|
||||
//fmt.Println(colNames, args)
|
||||
colPlaces = colPlaces[0 : len(colPlaces)-2]
|
||||
|
||||
sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)",
|
||||
|
@ -2575,7 +2591,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
handleAfterInsertProcessorFunc(bean)
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
@ -2634,7 +2650,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
handleAfterInsertProcessorFunc(bean)
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
@ -2687,7 +2703,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
|
||||
// Method InsertOne insert only one struct into database as a record.
|
||||
// The in parameter bean must a struct or a point to struct. The return
|
||||
// parameter is lastInsertId and error
|
||||
// parameter is inserted and error
|
||||
func (session *Session) InsertOne(bean interface{}) (int64, error) {
|
||||
err := session.newDb()
|
||||
if err != nil {
|
||||
|
@ -2747,7 +2763,7 @@ func (session *Session) cacheInsert(tables ...string) error {
|
|||
}
|
||||
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(table.Type)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
|
||||
for _, t := range tables {
|
||||
session.Engine.LogDebug("cache clear:", t)
|
||||
|
@ -2781,7 +2797,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
|
|||
}
|
||||
}
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(table.Type)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
tableName := session.Statement.TableName()
|
||||
session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:])
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:])
|
||||
|
@ -2906,7 +2922,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
|
||||
if session.Statement.ColumnStr == "" {
|
||||
colNames, args = buildConditions(session.Engine, table, bean, false, false,
|
||||
false, false, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, false, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
} else {
|
||||
colNames, args, err = genCols(table, session, bean, true, true)
|
||||
if err != nil {
|
||||
|
@ -2940,7 +2957,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
|
||||
if len(condiBean) > 0 {
|
||||
condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
}
|
||||
|
||||
var condition = ""
|
||||
|
@ -3019,7 +3037,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
return 0, err
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(t); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
//session.cacheUpdate(sqlStr, args...)
|
||||
cacher.ClearIds(session.Statement.TableName())
|
||||
cacher.ClearBeans(session.Statement.TableName())
|
||||
|
@ -3071,7 +3089,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
|
|||
return ErrCacheFailed
|
||||
}
|
||||
|
||||
cacher := session.Engine.getCacher(session.Statement.RefTable.Type)
|
||||
cacher := session.Engine.getCacher2(session.Statement.RefTable)
|
||||
tableName := session.Statement.TableName()
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
|
||||
if err != nil {
|
||||
|
@ -3137,7 +3155,8 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
table := session.Engine.autoMap(bean)
|
||||
session.Statement.RefTable = table
|
||||
colNames, args := buildConditions(session.Engine, table, bean, true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
|
||||
var condition = ""
|
||||
|
||||
|
@ -3167,7 +3186,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
|
||||
args = append(session.Statement.Params, args...)
|
||||
|
||||
if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheDelete(sqlStr, args...)
|
||||
}
|
||||
|
||||
|
|
78
statement.go
78
statement.go
|
@ -1,15 +1,13 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
//"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
//"strconv"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type inParam struct {
|
||||
|
@ -32,6 +30,7 @@ type Statement struct {
|
|||
HavingStr string
|
||||
ColumnStr string
|
||||
columnMap map[string]bool
|
||||
useAllCols bool
|
||||
OmitStr string
|
||||
ConditionStr string
|
||||
AltTableName string
|
||||
|
@ -47,7 +46,7 @@ type Statement struct {
|
|||
IsDistinct bool
|
||||
allUseBool bool
|
||||
checkVersion bool
|
||||
boolColumnMap map[string]bool
|
||||
mustColumnMap map[string]bool
|
||||
inColumns map[string]*inParam
|
||||
}
|
||||
|
||||
|
@ -68,6 +67,7 @@ func (statement *Statement) Init() {
|
|||
statement.columnMap = make(map[string]bool)
|
||||
statement.ConditionStr = ""
|
||||
statement.AltTableName = ""
|
||||
statement.IdParam = nil
|
||||
statement.RawSQL = ""
|
||||
statement.RawParams = make([]interface{}, 0)
|
||||
statement.BeanArgs = make([]interface{}, 0)
|
||||
|
@ -75,7 +75,7 @@ func (statement *Statement) Init() {
|
|||
statement.UseAutoTime = true
|
||||
statement.IsDistinct = false
|
||||
statement.allUseBool = false
|
||||
statement.boolColumnMap = make(map[string]bool)
|
||||
statement.mustColumnMap = make(map[string]bool)
|
||||
statement.checkVersion = true
|
||||
statement.inColumns = make(map[string]*inParam)
|
||||
}
|
||||
|
@ -118,11 +118,12 @@ func (statement *Statement) Or(querystring string, args ...interface{}) *Stateme
|
|||
|
||||
// tempororily set table name
|
||||
func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
||||
t := rType(tableNameOrBean)
|
||||
v := rValue(tableNameOrBean)
|
||||
t := v.Type()
|
||||
if t.Kind() == reflect.String {
|
||||
statement.AltTableName = tableNameOrBean.(string)
|
||||
} else if t.Kind() == reflect.Struct {
|
||||
statement.RefTable = statement.Engine.autoMapType(t)
|
||||
statement.RefTable = statement.Engine.autoMapType(v)
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
@ -245,8 +246,9 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
|||
|
||||
// Auto generating conditions according a struct
|
||||
func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
||||
includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool,
|
||||
boolColumnMap map[string]bool) ([]string, []interface{}) {
|
||||
includeVersion bool, includeUpdated bool, includeNil bool,
|
||||
includeAutoIncr bool, allUseBool bool, useAllCols bool,
|
||||
mustColumnMap map[string]bool) ([]string, []interface{}) {
|
||||
|
||||
colNames := make([]string, 0)
|
||||
var args = make([]interface{}, 0)
|
||||
|
@ -274,7 +276,15 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
fieldValue := *fieldValuePtr
|
||||
fieldType := reflect.TypeOf(fieldValue.Interface())
|
||||
|
||||
requiredField := false
|
||||
requiredField := useAllCols
|
||||
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
|
||||
if b {
|
||||
requiredField = true
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
if fieldValue.IsNil() {
|
||||
if includeNil {
|
||||
|
@ -297,8 +307,6 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
case reflect.Bool:
|
||||
if allUseBool || requiredField {
|
||||
val = fieldValue.Interface()
|
||||
} else if _, ok := boolColumnMap[col.Name]; ok {
|
||||
val = fieldValue.Interface()
|
||||
} else {
|
||||
// if a bool in a struct, it will not be as a condition because it default is false,
|
||||
// please use Where() instead
|
||||
|
@ -346,7 +354,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
val = t
|
||||
}
|
||||
} else {
|
||||
engine.autoMapType(fieldValue.Type())
|
||||
engine.autoMapType(fieldValue)
|
||||
if table, ok := engine.Tables[fieldValue.Type()]; ok {
|
||||
if len(table.PrimaryKeys) == 1 {
|
||||
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
|
||||
|
@ -534,13 +542,34 @@ func (statement *Statement) Cols(columns ...string) *Statement {
|
|||
return statement
|
||||
}
|
||||
|
||||
// Update use only: update all columns
|
||||
func (statement *Statement) AllCols() *Statement {
|
||||
statement.useAllCols = true
|
||||
return statement
|
||||
}
|
||||
|
||||
// Update use only: must update columns
|
||||
func (statement *Statement) MustCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.mustColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Update use only: not update columns
|
||||
/*func (statement *Statement) NotCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.mustColumnMap[strings.ToLower(nc)] = false
|
||||
}
|
||||
return statement
|
||||
}*/
|
||||
|
||||
// indicates that use bool fields as update contents and query contiditions
|
||||
func (statement *Statement) UseBool(columns ...string) *Statement {
|
||||
if len(columns) > 0 {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.boolColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
statement.MustCols(columns...)
|
||||
} else {
|
||||
statement.allUseBool = true
|
||||
}
|
||||
|
@ -676,13 +705,7 @@ func (s *Statement) genDelIndexSQL() []string {
|
|||
}
|
||||
|
||||
func (s *Statement) genDropSQL() string {
|
||||
if s.Engine.dialect.DBType() == core.MSSQL {
|
||||
return "IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'" +
|
||||
s.TableName() + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1) " +
|
||||
"DROP TABLE " + s.Engine.Quote(s.TableName()) + ";"
|
||||
} else {
|
||||
return "DROP TABLE IF EXISTS " + s.Engine.Quote(s.TableName()) + ";"
|
||||
}
|
||||
return s.Engine.dialect.DropTableSql(s.TableName()) + ";"
|
||||
}
|
||||
|
||||
func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
|
||||
|
@ -690,7 +713,8 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
|
|||
statement.RefTable = table
|
||||
|
||||
colNames, args := buildConditions(statement.Engine, table, bean, true, true,
|
||||
false, true, statement.allUseBool, statement.boolColumnMap)
|
||||
false, true, statement.allUseBool, statement.useAllCols,
|
||||
statement.mustColumnMap)
|
||||
|
||||
statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
statement.BeanArgs = args
|
||||
|
@ -729,7 +753,7 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
|
|||
statement.RefTable = table
|
||||
|
||||
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false,
|
||||
true, statement.allUseBool, statement.boolColumnMap)
|
||||
true, statement.allUseBool, statement.useAllCols, statement.mustColumnMap)
|
||||
|
||||
statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
statement.BeanArgs = args
|
||||
|
|
|
@ -271,7 +271,7 @@ func insertTwoTable(engine *xorm.Engine, t *testing.T) {
|
|||
}
|
||||
|
||||
type Article struct {
|
||||
Id int32 `xorm:"pk INT autoincr`
|
||||
Id int32 `xorm:"pk INT autoincr"`
|
||||
Name string `xorm:"VARCHAR(45)"`
|
||||
Img string `xorm:"VARCHAR(100)"`
|
||||
Aside string `xorm:"VARCHAR(200)"`
|
||||
|
@ -334,31 +334,143 @@ func update(engine *xorm.Engine, t *testing.T) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
cnt, err = engine.Insert(&Article{0, "1", "2", "3", "4", "5", 2})
|
||||
defer func() {
|
||||
err = engine.DropTables(&Article{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
a := &Article{0, "1", "2", "3", "4", "5", 2}
|
||||
cnt, err = engine.Insert(a)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if cnt != 1 {
|
||||
err = errors.New("insert not returned 1")
|
||||
err = errors.New(fmt.Sprintf("insert not returned 1 but %d", cnt))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
cnt, err = engine.Id(1).Update(&Article{Name: "6"})
|
||||
if a.Id == 0 {
|
||||
err = errors.New("insert returned id is 0")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cnt, err = engine.Id(a.Id).Update(&Article{Name: "6"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if cnt != 1 {
|
||||
err = errors.New("update not returned 1")
|
||||
err = errors.New(fmt.Sprintf("insert not returned 1 but %d", cnt))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateAllCols struct {
|
||||
Id int64
|
||||
Bool bool
|
||||
String string
|
||||
}
|
||||
|
||||
col1 := &UpdateAllCols{}
|
||||
err = engine.Sync(col1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = engine.Insert(col1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
col2 := &UpdateAllCols{col1.Id, true, ""}
|
||||
_, err = engine.Id(col2.Id).AllCols().Update(col2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
col3 := &UpdateAllCols{}
|
||||
has, err := engine.Id(col2.Id).Get(col3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !has {
|
||||
err = errors.New(fmt.Sprintf("cannot get id %d", col2.Id))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
if *col2 != *col3 {
|
||||
err = errors.New(fmt.Sprintf("col2 should eq col3"))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
{
|
||||
type UpdateMustCols struct {
|
||||
Id int64
|
||||
Bool bool
|
||||
String string
|
||||
}
|
||||
|
||||
col1 := &UpdateMustCols{}
|
||||
err = engine.Sync(col1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = engine.Insert(col1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
col2 := &UpdateMustCols{col1.Id, true, ""}
|
||||
boolStr := engine.columnMapper.Obj2Table("Bool")
|
||||
stringStr := engine.columnMapper.Obj2Table("String")
|
||||
_, err = engine.Id(col2.Id).MustCols(boolStr, stringStr).Update(col2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
col3 := &UpdateMustCols{}
|
||||
has, err := engine.Id(col2.Id).Get(col3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !has {
|
||||
err = errors.New(fmt.Sprintf("cannot get id %d", col2.Id))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
|
||||
if *col2 != *col3 {
|
||||
err = errors.New(fmt.Sprintf("col2 should eq col3"))
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateSameMapper(engine *xorm.Engine, t *testing.T) {
|
||||
|
@ -612,25 +724,54 @@ func where(engine *xorm.Engine, t *testing.T) {
|
|||
|
||||
func in(engine *xorm.Engine, t *testing.T) {
|
||||
users := make([]Userinfo, 0)
|
||||
err := engine.In("(id)", 1, 2, 3).Find(&users)
|
||||
err := engine.In("(id)", 7, 8, 9).Find(&users)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(users)
|
||||
if len(users) != 3 {
|
||||
err = errors.New("in uses should be 7,8,9 total 3")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Uid != 7 && user.Uid != 8 && user.Uid != 9 {
|
||||
err = errors.New("in uses should be 7,8,9 total 3")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
users = make([]Userinfo, 0)
|
||||
ids := []interface{}{7, 8, 9}
|
||||
err = engine.Where("departname = ?", "dev").In("(id)", ids...).Find(&users)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(users)
|
||||
|
||||
ids := []interface{}{1, 2, 3}
|
||||
err = engine.Where("(id) > ?", 2).In("(id)", ids...).Find(&users)
|
||||
if err != nil {
|
||||
if len(users) != 3 {
|
||||
err = errors.New("in uses should be 7,8,9 total 3")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(users)
|
||||
|
||||
for _, user := range users {
|
||||
if user.Uid != 7 && user.Uid != 8 && user.Uid != 9 {
|
||||
err = errors.New("in uses should be 7,8,9 total 3")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
department := engine.ColumnMapper.Obj2Table("Departname")
|
||||
dev := engine.ColumnMapper.Obj2Table("Dev")
|
||||
|
||||
err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
|
@ -3771,6 +3912,39 @@ func testCompositeKey(engine *xorm.Engine, t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type Lowercase struct {
|
||||
Id int64
|
||||
Name string
|
||||
ended int64 `xorm:"-"`
|
||||
}
|
||||
|
||||
func testLowerCase(engine *Engine, t *testing.T) {
|
||||
err := engine.Sync(&Lowercase{})
|
||||
_, err = engine.Where("id > 0").Delete(&Lowercase{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
_, err = engine.Insert(&Lowercase{ended: 1})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ls := make([]Lowercase, 0)
|
||||
err = engine.Find(&ls)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(ls) != 1 {
|
||||
err = errors.New("should be 1")
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
UserId string `xorm:"varchar(19) not null pk"`
|
||||
NickName string `xorm:"varchar(19) not null"`
|
||||
|
@ -3779,8 +3953,8 @@ type User struct {
|
|||
}
|
||||
|
||||
func testCompositeKey2(engine *xorm.Engine, t *testing.T) {
|
||||
|
||||
err := engine.DropTables(&User{})
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
panic(err)
|
||||
|
@ -3836,7 +4010,29 @@ func testCompositeKey2(engine *xorm.Engine, t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testAll(engine *xorm.Engine, t *testing.T) {
|
||||
type CustomTableName struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c *CustomTableName) TableName() string {
|
||||
return "customtablename"
|
||||
}
|
||||
|
||||
func testCustomTableName(engine *Engine, t *testing.T) {
|
||||
c := new(CustomTableName)
|
||||
err := engine.DropTables(c)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = engine.CreateTables(c)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testAll(engine *Engine, t *testing.T) {
|
||||
fmt.Println("-------------- directCreateTable --------------")
|
||||
directCreateTable(engine, t)
|
||||
fmt.Println("-------------- insert --------------")
|
||||
|
@ -3924,10 +4120,14 @@ func testAll2(engine *xorm.Engine, t *testing.T) {
|
|||
testPrefixTableName(engine, t)
|
||||
fmt.Println("-------------- testCreatedUpdated --------------")
|
||||
testCreatedUpdated(engine, t)
|
||||
fmt.Println("-------------- testLowercase ---------------")
|
||||
testLowerCase(engine, t)
|
||||
fmt.Println("-------------- processors --------------")
|
||||
testProcessors(engine, t)
|
||||
fmt.Println("-------------- transaction --------------")
|
||||
transaction(engine, t)
|
||||
fmt.Println("-------------- testCustomTableName --------------")
|
||||
testCustomTableName(engine, t)
|
||||
}
|
||||
|
||||
// !nash! the 3rd set of the test is intended for non-cache enabled engine
|
||||
|
|
11
xorm.go
11
xorm.go
|
@ -9,10 +9,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/lunny/xorm/core"
|
||||
_ "github.com/lunny/xorm/dialects"
|
||||
_ "github.com/lunny/xorm/drivers"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm/caches"
|
||||
_ "github.com/go-xorm/xorm/dialects"
|
||||
_ "github.com/go-xorm/xorm/drivers"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -50,13 +50,14 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
|||
DriverName: driverName,
|
||||
DataSourceName: dataSourceName,
|
||||
dialect: dialect,
|
||||
tableCachers: make(map[reflect.Type]core.Cacher)}
|
||||
}
|
||||
|
||||
engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
|
||||
|
||||
engine.Filters = dialect.Filters()
|
||||
|
||||
engine.Tables = make(map[reflect.Type]*core.Table)
|
||||
|
||||
engine.mutex = &sync.RWMutex{}
|
||||
engine.TagIdentifier = "xorm"
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
|
@ -238,7 +239,7 @@ func tag(table *core.Table, col *core.Column) string {
|
|||
nstr := col.SQLType.Name
|
||||
if col.Length != 0 {
|
||||
if col.Length2 != 0 {
|
||||
nstr += fmt.Sprintf("(%v, %v)", col.Length, col.Length2)
|
||||
nstr += fmt.Sprintf("(%v,%v)", col.Length, col.Length2)
|
||||
} else {
|
||||
nstr += fmt.Sprintf("(%v)", col.Length)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings" //[SWH|+]
|
||||
"text/template"
|
||||
|
||||
_ "github.com/bylevel/pq"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/core"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
)
|
||||
|
@ -93,12 +95,14 @@ func runReverse(cmd *Command, args []string) {
|
|||
var genDir string
|
||||
var model string
|
||||
if len(args) == 4 {
|
||||
|
||||
genDir, err = filepath.Abs(args[3])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
//[SWH|+] 经测试,path.Base不能解析windows下的“\”,需要替换为“/”
|
||||
genDir = strings.Replace(genDir, "\\", "/", -1)
|
||||
model = path.Base(genDir)
|
||||
} else {
|
||||
model = "model"
|
||||
|
@ -119,6 +123,7 @@ func runReverse(cmd *Command, args []string) {
|
|||
var langTmpl LangTmpl
|
||||
var ok bool
|
||||
var lang string = "go"
|
||||
var prefix string = "" //[SWH|+]
|
||||
|
||||
cfgPath := path.Join(dir, "config")
|
||||
info, err := os.Stat(cfgPath)
|
||||
|
@ -131,6 +136,11 @@ func runReverse(cmd *Command, args []string) {
|
|||
if j, ok := configs["genJson"]; ok {
|
||||
genJson, err = strconv.ParseBool(j)
|
||||
}
|
||||
|
||||
//[SWH|+]
|
||||
if j, ok := configs["prefix"]; ok {
|
||||
prefix = j
|
||||
}
|
||||
}
|
||||
|
||||
if langTmpl, ok = langTmpls[lang]; !ok {
|
||||
|
@ -192,6 +202,10 @@ func runReverse(cmd *Command, args []string) {
|
|||
|
||||
tbls := make([]*core.Table, 0)
|
||||
for _, table := range tables {
|
||||
//[SWH|+]
|
||||
if prefix != "" {
|
||||
table.Name = strings.TrimPrefix(table.Name, prefix)
|
||||
}
|
||||
tbls = append(tbls, table)
|
||||
}
|
||||
|
||||
|
@ -224,6 +238,10 @@ func runReverse(cmd *Command, args []string) {
|
|||
w.Close()
|
||||
} else {
|
||||
for _, table := range tables {
|
||||
//[SWH|+]
|
||||
if prefix != "" {
|
||||
table.Name = strings.TrimPrefix(table.Name, prefix)
|
||||
}
|
||||
// imports
|
||||
tbs := []*core.Table{table}
|
||||
imports := langTmpl.GenImports(tbs)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
lang=go
|
||||
genJson=0
|
||||
prefix=cos_
|
||||
|
|
Loading…
Reference in New Issue