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:
Lunny Xiao 2014-04-11 15:37:27 +08:00
commit 5080b2b571
46 changed files with 709 additions and 2483 deletions

View File

@ -1,14 +1,17 @@
## Contributing to xorm ## 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, much welcome. You can help with patch review, submitting bug reports,
or adding new functionality. There is no formal style guide, but or adding new functionality. There is no formal style guide, but
please conform to the style of existing code and general Go formatting please conform to the style of existing code and general Go formatting
conventions when submitting patches. 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 ### 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. proposed functionality.
### Bug reports ### Bug reports

27
LICENSE Normal file
View File

@ -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.

View File

@ -76,17 +76,19 @@ Or
# Cases # 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) * [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) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
# Todo * [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
[Todo List](https://trello.com/b/IHsuAnhk/xorm)
# Discuss # Discuss
@ -102,4 +104,4 @@ If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/
# LICENSE # LICENSE
BSD License BSD License
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)

View File

@ -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) * [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) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
## Todo * [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
[开发计划](https://trello.com/b/IHsuAnhk/xorm)
## 讨论 ## 讨论

View File

@ -7,7 +7,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type LRUCacher struct { type LRUCacher struct {

View File

@ -5,7 +5,7 @@ import (
"errors" "errors"
"sync" "sync"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
var ( var (

View File

@ -1 +0,0 @@
go test -v -bench=. -run=XXX

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -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)
}
}
}

View File

@ -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]
}

View File

@ -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]
}

View File

@ -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
}

View File

@ -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)}
}

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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("")
}
}

View File

@ -1,16 +1,12 @@
package dialects package dialects
import ( import (
//"crypto/tls"
"errors" "errors"
"fmt" "fmt"
//"regexp"
"strconv" "strconv"
"strings" "strings"
//"time"
. "github.com/lunny/xorm/core" . "github.com/go-xorm/core"
) )
func init() { func init() {
@ -90,6 +86,12 @@ func (db *mssql) AutoIncrStr() string {
return "IDENTITY" 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 { func (db *mssql) SupportCharset() bool {
return false return false
} }
@ -118,8 +120,8 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*Column, error) { func (db *mssql) GetColumns(tableName string) ([]string, map[string]*Column, error) {
args := []interface{}{} args := []interface{}{}
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale
from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id
where a.object_id=object_id('` + tableName + `')` where a.object_id=object_id('` + tableName + `')`
cnn, err := Open(db.DriverName(), db.DataSourceName()) cnn, err := Open(db.DriverName(), db.DataSourceName())
if err != nil { if err != nil {
@ -166,6 +168,10 @@ where a.object_id=object_id('` + tableName + `')`
if col.SQLType.IsText() { if col.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col
@ -203,18 +209,18 @@ func (db *mssql) GetTables() ([]*Table, error) {
func (db *mssql) GetIndexes(tableName string) (map[string]*Index, error) { func (db *mssql) GetIndexes(tableName string) (map[string]*Index, error) {
args := []interface{}{tableName} args := []interface{}{tableName}
s := `SELECT s := `SELECT
IXS.NAME AS [INDEX_NAME], IXS.NAME AS [INDEX_NAME],
C.NAME AS [COLUMN_NAME], C.NAME AS [COLUMN_NAME],
IXS.is_unique AS [IS_UNIQUE], IXS.is_unique AS [IS_UNIQUE],
CASE IXCS.IS_INCLUDED_COLUMN CASE IXCS.IS_INCLUDED_COLUMN
WHEN 0 THEN 'NONE' WHEN 0 THEN 'NONE'
ELSE 'INCLUDED' END AS [IS_INCLUDED_COLUMN] ELSE 'INCLUDED' END AS [IS_INCLUDED_COLUMN]
FROM SYS.INDEXES IXS FROM SYS.INDEXES IXS
INNER JOIN SYS.INDEX_COLUMNS IXCS INNER JOIN SYS.INDEX_COLUMNS IXCS
ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
AND IXCS.COLUMN_ID=C.COLUMN_ID AND IXCS.COLUMN_ID=C.COLUMN_ID
WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
` `
cnn, err := Open(db.DriverName(), db.DataSourceName()) cnn, err := Open(db.DriverName(), db.DataSourceName())

View File

@ -8,7 +8,7 @@ import (
"strings" "strings"
"time" "time"
. "github.com/lunny/xorm/core" . "github.com/go-xorm/core"
) )
func init() { func init() {
@ -37,6 +37,7 @@ func (db *mysql) SqlType(c *Column) string {
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case Bool: case Bool:
res = TinyInt res = TinyInt
c.Length = 1
case Serial: case Serial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true

View File

@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
. "github.com/lunny/xorm/core" . "github.com/go-xorm/core"
) )
func init() { func init() {
@ -150,6 +150,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
if col.SQLType.IsText() { if col.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col

View File

@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
. "github.com/lunny/xorm/core" . "github.com/go-xorm/core"
) )
func init() { func init() {
@ -127,6 +127,7 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
for rows.Next() { for rows.Next() {
col := new(Column) col := new(Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
var colName, isNullable, dataType string var colName, isNullable, dataType string
var maxLenStr, colDefault, numPrecision, numRadix *string var maxLenStr, colDefault, numPrecision, numRadix *string
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix) 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.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col

View File

@ -3,7 +3,7 @@ package dialects
import ( import (
"strings" "strings"
. "github.com/lunny/xorm/core" . "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -3,8 +3,8 @@ xorm 快速入门
* [1.创建Orm引擎](#10) * [1.创建Orm引擎](#10)
* [2.定义表结构体](#20) * [2.定义表结构体](#20)
* [2.1.名称映射规则](#21) * [2.1.名称映射规则](#21)
* [2.2.前缀映射规则和后缀映射规则](#22) * [2.2.前缀映射,后缀映射和缓存映射](#22)
* [2.3.使用Table和Tag改变名称映射](#23) * [2.3.使用Table和Tag改变名称映射](#23)
* [2.4.Column属性定义](#24) * [2.4.Column属性定义](#24)
* [2.5.Go与字段类型对应表](#25) * [2.5.Go与字段类型对应表](#25)
@ -20,7 +20,7 @@ xorm 快速入门
* [5.3.Get方法](#63) * [5.3.Get方法](#63)
* [5.4.Find方法](#64) * [5.4.Find方法](#64)
* [5.5.Iterate方法](#65) * [5.5.Iterate方法](#65)
* [5.6.Count方法](#66) * [5.6.Count方法](#66)
* [5.7.Rows方法](#67) * [5.7.Rows方法](#67)
* [6.更新数据](#70) * [6.更新数据](#70)
* [6.1.乐观锁](#71) * [6.1.乐观锁](#71)
@ -29,12 +29,13 @@ xorm 快速入门
* [9.执行SQL命令](#100) * [9.执行SQL命令](#100)
* [10.事务处理](#110) * [10.事务处理](#110)
* [11.缓存](#120) * [11.缓存](#120)
* [12.xorm工具](#130) * [12.事件](#125)
* [12.1.反转命令](#131) * [13.xorm工具](#130)
* [13.Examples](#140) * [13.1.反转命令](#131)
* [14.案例](#150) * [14.Examples](#140)
* [15.那些年我们踩过的坑](#160) * [15.案例](#150)
* [16.讨论](#170) * [16.那些年我们踩过的坑](#160)
* [17.讨论](#170)
<a name="10" id="10"></a> <a name="10" id="10"></a>
## 1.创建Orm引擎 ## 1.创建Orm引擎
@ -45,7 +46,7 @@ xorm 快速入门
import ( import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/lunny/xorm"
) )
engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8") engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
defer engine.Close() defer engine.Close()
``` ```
@ -58,20 +59,23 @@ import (
"github.com/lunny/xorm" "github.com/lunny/xorm"
) )
engine, err = xorm.NewEngine("sqlite3", "./test.db") engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
``` ```
一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
xorm当前支持五种驱动四个数据库如下 xorm当前支持五种驱动四个数据库如下
<<<<<<< HEAD
=======
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
>>>>>>> master
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq) * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc) * MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc)
@ -120,27 +124,39 @@ engine.SetMapper(SameMapper{})
同时需要注意的是: 同时需要注意的是:
<<<<<<< HEAD
* 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。 * 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
=======
* 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
>>>>>>> master
```Go ```Go
engine.SetTableMapper(SameMapper{}) engine.SetTableMapper(SameMapper{})
engine.SetColumnMapper(SnakeMapper{}) engine.SetColumnMapper(SnakeMapper{})
``` ```
<a name="22" id="22"></a> <a name="22" id="22"></a>
### 2.2.前缀映射规则和后缀映射规则 ### 2.2.前缀映射,后缀映射和缓存映射
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
<<<<<<< HEAD
* *
=======
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。
>>>>>>> master
<a name="23" id="23"></a> <a name="23" id="23"></a>
### 2.3.使用Table和Tag改变名称映射 ### 2.3.使用Table和Tag改变名称映射
如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。 如果所有的命名都是按照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> <a name="23" id="23"></a>
### 2.4.Column属性定义 ### 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中的关键字均不区分大小写字段名区分大小写 具体的映射规则如下另Tag中的关键字均不区分大小写字段名区分大小写
@ -356,7 +372,7 @@ affected, err := engine.Insert(user, &questions)
``` ```
这里需要注意以下几点: 这里需要注意以下几点:
* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。 * 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。
* 多条插入会自动生成`Insert into table values (),(),()`的语句因此这样的语句有一个最大的记录数根据经验测算在150条左右。大于150条后生成的sql语句将太长可能导致执行失败。因此在插入大量数据时目前需要自行分割成每150条插入一次。 * 多条插入会自动生成`Insert into table values (),(),()`的语句因此这样的语句有一个最大的记录数根据经验测算在150条左右。大于150条后生成的sql语句将太长可能导致执行失败。因此在插入大量数据时目前需要自行分割成每150条插入一次。
<a name="60" id="60"></a> <a name="60" id="60"></a>
@ -407,7 +423,11 @@ engine.Cols("age", "name").Update(&user)
// UPDATE user SET age=? AND name=? // UPDATE user SET age=? AND name=?
``` ```
其中的参数"age", "name"也可以写成"age, name",两种写法均可 * AllCols()
查询或更新所有字段。
* MustCols(…string)
某些字段必须更新。
* Omit(...string) * Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用 和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
@ -504,7 +524,7 @@ has, err := engine.Get(user)
1) 传入Slice用于返回数据 1) 传入Slice用于返回数据
```Go ```Go
everyone := make([]Userinfo, 0) everyone := make([]Userinfo, 0)
err := engine.Find(&everyone) err := engine.Find(&everyone)
pEveryOne := make([]*Userinfo, 0) pEveryOne := make([]*Userinfo, 0)
@ -578,13 +598,21 @@ affected, err := engine.Id(id).Update(user)
这里需要注意Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容因此如果需要更新一个值为0则此种方法将无法实现因此有两种选择 这里需要注意Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容因此如果需要更新一个值为0则此种方法将无法实现因此有两种选择
<<<<<<< HEAD
1. 通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。 1. 通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。
=======
* 1.通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。
>>>>>>> master
```Go ```Go
affected, err := engine.Id(id).Cols("age").Update(&user) affected, err := engine.Id(id).Cols("age").Update(&user)
``` ```
<<<<<<< HEAD
2. 通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。 2. 通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。
=======
* 2.通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。
>>>>>>> master
```Go ```Go
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
@ -643,7 +671,7 @@ results, err := engine.Query(sql)
```Go ```Go
sql = "update `userinfo` set username=? where id=?" sql = "update `userinfo` set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1) res, err := engine.Exec(sql, "xiaolun", 1)
``` ```
<a name="110" id="110"></a> <a name="110" id="110"></a>
@ -678,9 +706,11 @@ if err != nil {
err = session.Commit() err = session.Commit()
if err != nil { if err != nil {
return return
} }
``` ```
* 注意如果您使用的是mysql数据库引擎为innodb事务才有效myisam引擎是不支持事务的。
<a name="120" id="120"></a> <a name="120" id="120"></a>
## 11.缓存 ## 11.缓存
@ -715,7 +745,7 @@ engine.MapCacher(&user, nil)
1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存 1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存
2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。 2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。
3. 在使用Exec方法执行了方法之后可能会导致缓存与数据库不一致的地方。因此如果启用缓存尽量避免使用Exec。如果必须使用则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如 3. 在使用Exec方法执行了方法之后可能会导致缓存与数据库不一致的地方。因此如果启用缓存尽量避免使用Exec。如果必须使用则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如
```Go ```Go
@ -727,20 +757,46 @@ engine.ClearCache(new(User))
![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) ![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png)
<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> <a name="130" id="130"></a>
## 12.xorm工具 ## 13.xorm工具
xorm工具提供了xorm命令能够帮助做很多事情。 xorm工具提供了xorm命令能够帮助做很多事情。
### 12.1.反转命令 ### 13.1.反转命令
参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm) 参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm)
<a name="140" id="140"></a> <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) 请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples)
<a name="150" id="150"></a> <a name="150" id="150"></a>
## 14.案例 ## 15.案例
* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) * [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
@ -751,15 +807,15 @@ xorm工具提供了xorm命令能够帮助做很多事情。
* [VeryHour](http://veryhour.com) * [VeryHour](http://veryhour.com)
<a name="160" id="160"></a> <a name="160" id="160"></a>
## 15.那些年我们踩过的坑 ## 16.那些年我们踩过的坑
* 怎么同时使用xorm的tag和json的tag * 怎么同时使用xorm的tag和json的tag
答:使用空格 答:使用空格
```Go ```Go
type User struct { type User struct {
Name string `json:"name" xorm:"name"` Name string `json:"name" xorm:"name"`
} }
``` ```
* 我的struct里面包含bool类型为什么它不能作为条件也没法用Update更新 * 我的struct里面包含bool类型为什么它不能作为条件也没法用Update更新
@ -795,5 +851,5 @@ money float64 `xorm:"Numeric"`
<a name="170" id="170"></a> <a name="170" id="170"></a>
## 16.讨论 ## 17.讨论
请加入QQ群280360085 进行讨论。 请加入QQ群280360085 进行讨论。

View File

@ -100,30 +100,47 @@ engine.Logger = f
<a name="20" id="20"></a> <a name="20" id="20"></a>
## 2.Define struct ## 2.Define struct
xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下 xorm map a struct to a database table, the rule is below.
<a name="21" id="21"></a> <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 ```Go
engine.Mapper = SameMapper{} 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。 当然如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
<a name="22" id="22"></a> <a name="22" id="22"></a>
### 2.2.使用Table和Tag改变名称映射 ### 2.3.使用Table和Tag改变名称映射
如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。 如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。
通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。 通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。
<a name="23" id="23"></a> <a name="23" id="23"></a>
### 2.3.Column属性定义 ### 2.4.Column属性定义
我们在field对应的Tag中对Column的一些属性进行定义定义的方法基本和我们写SQL定义表结构类似比如 我们在field对应的Tag中对Column的一些属性进行定义定义的方法基本和我们写SQL定义表结构类似比如
``` ```

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -1,7 +1,7 @@
package drivers package drivers
import ( import (
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

227
engine.go
View File

@ -12,7 +12,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
// Engine is the major struct of xorm, it means a database manager. // Engine is the major struct of xorm, it means a database manager.
@ -26,16 +26,15 @@ type Engine struct {
dialect core.Dialect dialect core.Dialect
Tables map[reflect.Type]*core.Table Tables map[reflect.Type]*core.Table
mutex *sync.RWMutex mutex *sync.RWMutex
ShowSQL bool ShowSQL bool
ShowErr bool ShowErr bool
ShowDebug bool ShowDebug bool
ShowWarn bool ShowWarn bool
Pool IConnectPool Pool IConnectPool
Filters []core.Filter Filters []core.Filter
Logger ILogger // io.Writer Logger ILogger // io.Writer
Cacher core.Cacher Cacher core.Cacher
tableCachers map[reflect.Type]core.Cacher
} }
func (engine *Engine) SetMapper(mapper core.IMapper) { func (engine *Engine) SetMapper(mapper core.IMapper) {
@ -117,9 +116,9 @@ func (engine *Engine) NoCascade() *Session {
// Set a table use a special cacher // Set a table use a special cacher
func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
t := rType(bean) v := rValue(bean)
engine.autoMapType(t) engine.autoMapType(v)
engine.tableCachers[t] = cacher engine.Tables[v.Type()].Cacher = cacher
} }
// OpenDB provides a interface to operate database directly. // OpenDB provides a interface to operate database directly.
@ -308,6 +307,18 @@ func (engine *Engine) Cols(columns ...string) *Session {
return session.Cols(columns...) 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 // Xorm automatically retrieve condition according struct, but
// if struct has bool field, it will ignore them. So use UseBool // if struct has bool field, it will ignore them. So use UseBool
// to tell system to do not ignore them. // to tell system to do not ignore them.
@ -395,12 +406,13 @@ func (engine *Engine) Having(conditions string) *Session {
return session.Having(conditions) 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() engine.mutex.RLock()
table, ok := engine.Tables[t] table, ok := engine.Tables[t]
engine.mutex.RUnlock() engine.mutex.RUnlock()
if !ok { if !ok {
table = engine.mapType(t) table = engine.mapType(v)
engine.mutex.Lock() engine.mutex.Lock()
engine.Tables[t] = table engine.Tables[t] = table
engine.mutex.Unlock() engine.mutex.Unlock()
@ -409,26 +421,66 @@ func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
} }
func (engine *Engine) autoMap(bean interface{}) *core.Table { func (engine *Engine) autoMap(bean interface{}) *core.Table {
t := rType(bean) v := rValue(bean)
return engine.autoMapType(t) 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) 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 { func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
table := core.NewEmptyTable() table := core.NewEmptyTable()
table.Name = tableMapper.Obj2Table(t.Name()) 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 table.Type = t
var idFieldColName string var idFieldColName string
var err error
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
tag := t.Field(i).Tag tag := t.Field(i).Tag
ormTagStr := tag.Get(tagId) ormTagStr := tag.Get(engine.TagIdentifier)
var col *core.Column var col *core.Column
fieldType := t.Field(i).Type fieldValue := v.Field(i)
fieldType := fieldValue.Type()
if ormTagStr != "" { if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, 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") && if (strings.ToUpper(tags[0]) == "EXTENDS") &&
(fieldType.Kind() == reflect.Struct) { (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() { for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName) col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
table.AddColumn(col) table.AddColumn(col)
@ -450,8 +504,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
continue continue
} }
var indexType int indexNames := make(map[string]int)
var indexName string var isIndex, isUnique bool
var preKey string
for j, key := range tags { for j, key := range tags {
k := strings.ToUpper(key) k := strings.ToUpper(key)
switch { switch {
@ -464,8 +519,18 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
col.Nullable = false col.Nullable = false
case k == "NULL": case k == "NULL":
col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") 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": case k == "AUTOINCR":
col.IsAutoIncrement = true col.IsAutoIncrement = true
//col.AutoIncrStart = 1
case k == "DEFAULT": case k == "DEFAULT":
col.Default = tags[j+1] col.Default = tags[j+1]
case k == "CREATED": case k == "CREATED":
@ -476,35 +541,46 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
case k == "UPDATED": case k == "UPDATED":
col.IsUpdated = true col.IsUpdated = true
case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): 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": case k == "INDEX":
indexType = core.IndexType isIndex = true
case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"): case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
indexName = k[len("UNIQUE")+1 : len(k)-1] indexName := k[len("UNIQUE")+1 : len(k)-1]
indexType = core.UniqueType indexNames[indexName] = core.UniqueType
case k == "UNIQUE": case k == "UNIQUE":
indexType = core.UniqueType isUnique = true
case k == "NOTNULL": case k == "NOTNULL":
col.Nullable = false col.Nullable = false
case k == "NOT": case k == "NOT":
default: default:
if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") { if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
if key != col.Default { if preKey != "DEFAULT" {
col.Name = key[1 : len(key)-1] col.Name = key[1 : len(key)-1]
} }
} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") { } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
fs := strings.Split(k, "(") fs := strings.Split(k, "(")
if _, ok := core.SqlTypes[fs[0]]; !ok { if _, ok := core.SqlTypes[fs[0]]; !ok {
preKey = k
continue continue
} }
col.SQLType = core.SQLType{fs[0], 0, 0} col.SQLType = core.SQLType{fs[0], 0, 0}
fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",") fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
if len(fs2) == 2 { if len(fs2) == 2 {
col.Length, _ = strconv.Atoi(fs2[0]) col.Length, err = strconv.Atoi(fs2[0])
col.Length2, _ = strconv.Atoi(fs2[1]) if err != nil {
engine.LogError(err)
}
col.Length2, err = strconv.Atoi(fs2[1])
if err != nil {
engine.LogError(err)
}
} else if len(fs2) == 1 { } 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 { } else {
if _, ok := core.SqlTypes[k]; ok { if _, ok := core.SqlTypes[k]; ok {
@ -513,8 +589,10 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
col.Name = key col.Name = key
} }
} }
dialect.SqlType(col) panic("broken")
//engine.dialect.SqlType(col)
} }
preKey = k
} }
if col.SQLType.Name == "" { if col.SQLType.Name == "" {
col.SQLType = core.Type2SQLType(fieldType) col.SQLType = core.Type2SQLType(fieldType)
@ -527,40 +605,24 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
} }
//fmt.Println("======", col) //fmt.Println("======", col)
if col.Name == "" { 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 == "" { if isUnique {
indexName = col.Name indexNames[col.Name] = core.UniqueType
} } else if isIndex {
if index, ok := table.Indexes[indexName]; ok { indexNames[col.Name] = core.IndexType
index.AddColumn(col.Name) }
col.Indexes[index.Name] = true
} else { for indexName, indexType := range indexNames {
index := core.NewIndex(indexName, core.IndexType) addIndex(indexName, table, col, 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
}
} }
} }
} else { } else {
sqlType := core.Type2SQLType(fieldType) sqlType := core.Type2SQLType(fieldType)
col = core.NewColumn(colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
sqlType.DefaultLength, sqlType.DefaultLength2, true) t.Field(i).Name, sqlType, sqlType.DefaultLength,
sqlType.DefaultLength2, true)
} }
if col.IsAutoIncrement { if col.IsAutoIncrement {
col.Nullable = false col.Nullable = false
@ -590,19 +652,20 @@ func (engine *Engine) mapping(beans ...interface{}) (e error) {
engine.mutex.Lock() engine.mutex.Lock()
defer engine.mutex.Unlock() defer engine.mutex.Unlock()
for _, bean := range beans { for _, bean := range beans {
t := rType(bean) v := rValue(bean)
engine.Tables[t] = engine.mapType(t) engine.Tables[v.Type()] = engine.mapType(v)
} }
return return
} }
// If a table has any reocrd // If a table has any reocrd
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) { func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
t := rType(bean) v := rValue(bean)
t := v.Type()
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
return false, errors.New("bean should be a struct or struct's point") return false, errors.New("bean should be a struct or struct's point")
} }
engine.autoMapType(t) engine.autoMapType(v)
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
rows, err := session.Count(bean) rows, err := session.Count(bean)
@ -611,11 +674,11 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
// If a table is exist // If a table is exist
func (engine *Engine) IsTableExist(bean interface{}) (bool, error) { func (engine *Engine) IsTableExist(bean interface{}) (bool, error) {
t := rType(bean) v := rValue(bean)
if t.Kind() != reflect.Struct { if v.Type().Kind() != reflect.Struct {
return false, errors.New("bean should be a struct or struct's point") return false, errors.New("bean should be a struct or struct's point")
} }
table := engine.autoMapType(t) table := engine.autoMapType(v)
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
has, err := session.isTableExist(table.Name) has, err := session.isTableExist(table.Name)
@ -654,9 +717,13 @@ func (engine *Engine) CreateUniques(bean interface{}) error {
return session.CreateUniques(bean) return session.CreateUniques(bean)
} }
func (engine *Engine) getCacher(t reflect.Type) core.Cacher { func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
if cacher, ok := engine.tableCachers[t]; ok { return table.Cacher
return cacher }
func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
if table := engine.autoMapType(v); table != nil {
return table.Cacher
} }
return engine.Cacher return engine.Cacher
} }
@ -668,7 +735,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
return errors.New("error params") return errors.New("error params")
} }
table := engine.autoMap(bean) table := engine.autoMap(bean)
cacher := engine.getCacher(t) cacher := table.Cacher
if cacher == nil {
cacher = engine.Cacher
}
if cacher != nil { if cacher != nil {
cacher.ClearIds(table.Name) cacher.ClearIds(table.Name)
cacher.DelBean(table.Name, id) cacher.DelBean(table.Name, id)
@ -684,7 +754,10 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
return errors.New("error params") return errors.New("error params")
} }
table := engine.autoMap(bean) table := engine.autoMap(bean)
cacher := engine.getCacher(t) cacher := table.Cacher
if cacher == nil {
cacher = engine.Cacher
}
if cacher != nil { if cacher != nil {
cacher.ClearIds(table.Name) cacher.ClearIds(table.Name)
cacher.ClearBeans(table.Name) cacher.ClearBeans(table.Name)

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func indexNoCase(s, sep string) int { func indexNoCase(s, sep string) int {
@ -38,9 +38,14 @@ func makeArray(elem string, count int) []string {
return res return res
} }
func rValue(bean interface{}) reflect.Value {
return reflect.Indirect(reflect.ValueOf(bean))
}
func rType(bean interface{}) reflect.Type { func rType(bean interface{}) reflect.Type {
sliceValue := reflect.Indirect(reflect.ValueOf(bean)) 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 { func structName(v reflect.Type) string {

View File

@ -1,13 +1,12 @@
package xorm package xorm
import ( import (
"github.com/lunny/xorm/core"
//"fmt"
"sync"
//"sync/atomic"
"container/list" "container/list"
"reflect" "reflect"
"sync"
"time" "time"
"github.com/go-xorm/core"
) )
// Interface IConnecPool is a connection pool interface, all implements should implement // Interface IConnecPool is a connection pool interface, all implements should implement

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type Rows struct { type Rows struct {

View File

@ -11,7 +11,7 @@ import (
"strings" "strings"
"time" "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 // 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 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 { func (session *Session) NoCascade() *Session {
session.Statement.UseCascade = false session.Statement.UseCascade = false
return session return session
@ -290,7 +300,7 @@ func (session *Session) Begin() error {
// When using transaction, you can rollback if any error // When using transaction, you can rollback if any error
func (session *Session) Rollback() error { func (session *Session) Rollback() error {
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
session.Engine.logSQL("ROLL BACK") session.Engine.logSQL(session.Engine.dialect.RollBackStr())
session.IsCommitedOrRollbacked = true session.IsCommitedOrRollbacked = true
return session.Tx.Rollback() return session.Tx.Rollback()
} }
@ -357,13 +367,14 @@ func cleanupProcessorsClosures(slices *[]func(interface{})) {
} }
func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error { 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 { if dataStruct.Kind() != reflect.Struct {
return errors.New("Expected a pointer to a struct") return errors.New("Expected a pointer to a struct")
} }
table := session.Engine.autoMapType(rType(obj))
var col *core.Column var col *core.Column
table := session.Engine.autoMapType(dataStruct)
for key, data := range objMap { for key, data := range objMap {
if col = table.GetColumn(key); col == nil { 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())) 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 return false, ErrCacheFailed
} }
cacher := session.Engine.getCacher(session.Statement.RefTable.Type) cacher := session.Engine.getCacher2(session.Statement.RefTable)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args) session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args)
ids, err := core.GetCacheSql(cacher, tableName, 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 table := session.Statement.RefTable
cacher := session.Engine.getCacher(t) cacher := session.Engine.getCacher2(table)
ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args) ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args)
if err != nil { if err != nil {
//session.Engine.LogError(err) //session.Engine.LogError(err)
@ -927,6 +938,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
session.Statement.Limit(1) session.Statement.Limit(1)
var sqlStr string var sqlStr string
var args []interface{} var args []interface{}
session.Statement.RefTable = session.Engine.autoMap(bean) session.Statement.RefTable = session.Engine.autoMap(bean)
if session.Statement.RawSQL == "" { if session.Statement.RawSQL == "" {
@ -936,7 +948,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
args = session.Statement.RawParams 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...) has, err := session.cacheGet(bean, sqlStr, args...)
if err != ErrCacheFailed { if err != ErrCacheFailed {
return has, err return has, err
@ -1051,12 +1063,14 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
if session.Statement.RefTable == nil { if session.Statement.RefTable == nil {
if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Kind() == reflect.Ptr {
if sliceElementType.Elem().Kind() == reflect.Struct { if sliceElementType.Elem().Kind() == reflect.Struct {
table = session.Engine.autoMapType(sliceElementType.Elem()) pv := reflect.New(sliceElementType.Elem())
table = session.Engine.autoMapType(pv.Elem())
} else { } else {
return errors.New("slice type") return errors.New("slice type")
} }
} else if sliceElementType.Kind() == reflect.Struct { } else if sliceElementType.Kind() == reflect.Struct {
table = session.Engine.autoMapType(sliceElementType) pv := reflect.New(sliceElementType)
table = session.Engine.autoMapType(pv.Elem())
} else { } else {
return errors.New("slice type") return errors.New("slice type")
} }
@ -1067,7 +1081,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
if len(condiBean) > 0 { if len(condiBean) > 0 {
colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true, 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.ConditionStr = strings.Join(colNames, " AND ")
session.Statement.BeanArgs = args session.Statement.BeanArgs = args
} }
@ -1089,7 +1104,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
args = session.Statement.RawParams 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.UseCache &&
!session.Statement.IsDistinct { !session.Statement.IsDistinct {
err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) 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 { func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
dataStruct := rValue(bean)
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
if dataStruct.Kind() != reflect.Struct { if dataStruct.Kind() != reflect.Struct {
return errors.New("Expected a pointer to a 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)) scanResultContainers := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ { 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) fieldValue.Set(vv)
} }
} else if session.Statement.UseCascade { } else if session.Statement.UseCascade {
table := session.Engine.autoMapType(fieldValue.Type()) table := session.Engine.autoMapType(*fieldValue)
if table != nil { if table != nil {
var x int64 var x int64
if rawValueType.Kind() == reflect.Int64 { if rawValueType.Kind() == reflect.Int64 {
@ -1801,9 +1815,10 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
} }
bean := sliceValue.Index(0).Interface() 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 session.Statement.RefTable = table
size := sliceValue.Len() size := sliceValue.Len()
@ -1901,7 +1916,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
return 0, err 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()) session.cacheInsert(session.Statement.TableName())
} }
@ -2111,7 +2126,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
v = x v = x
fieldValue.Set(reflect.ValueOf(v)) fieldValue.Set(reflect.ValueOf(v))
} else if session.Statement.UseCascade { } else if session.Statement.UseCascade {
table := session.Engine.autoMapType(fieldValue.Type()) table := session.Engine.autoMapType(*fieldValue)
if table != nil { if table != nil {
x, err := strconv.ParseInt(string(data), 10, 64) x, err := strconv.ParseInt(string(data), 10, 64)
if err != nil { if err != nil {
@ -2524,6 +2539,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
} }
colPlaces := strings.Repeat("?, ", len(colNames)) colPlaces := strings.Repeat("?, ", len(colNames))
//fmt.Println(colNames, args)
colPlaces = colPlaces[0 : len(colPlaces)-2] colPlaces = colPlaces[0 : len(colPlaces)-2]
sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)", 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) 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()) session.cacheInsert(session.Statement.TableName())
} }
@ -2634,7 +2650,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
handleAfterInsertProcessorFunc(bean) 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()) 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. // 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 // 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) { func (session *Session) InsertOne(bean interface{}) (int64, error) {
err := session.newDb() err := session.newDb()
if err != nil { if err != nil {
@ -2747,7 +2763,7 @@ func (session *Session) cacheInsert(tables ...string) error {
} }
table := session.Statement.RefTable table := session.Statement.RefTable
cacher := session.Engine.getCacher(table.Type) cacher := session.Engine.getCacher2(table)
for _, t := range tables { for _, t := range tables {
session.Engine.LogDebug("cache clear:", t) session.Engine.LogDebug("cache clear:", t)
@ -2781,7 +2797,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
} }
} }
table := session.Statement.RefTable table := session.Statement.RefTable
cacher := session.Engine.getCacher(table.Type) cacher := session.Engine.getCacher2(table)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:]) session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:])
ids, err := core.GetCacheSql(cacher, tableName, 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 == "" { if session.Statement.ColumnStr == "" {
colNames, args = buildConditions(session.Engine, table, bean, false, false, 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 { } else {
colNames, args, err = genCols(table, session, bean, true, true) colNames, args, err = genCols(table, session, bean, true, true)
if err != nil { if err != nil {
@ -2940,7 +2957,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
if len(condiBean) > 0 { if len(condiBean) > 0 {
condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true, 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 = "" var condition = ""
@ -3019,7 +3037,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
return 0, err 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...) //session.cacheUpdate(sqlStr, args...)
cacher.ClearIds(session.Statement.TableName()) cacher.ClearIds(session.Statement.TableName())
cacher.ClearBeans(session.Statement.TableName()) cacher.ClearBeans(session.Statement.TableName())
@ -3071,7 +3089,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
return ErrCacheFailed return ErrCacheFailed
} }
cacher := session.Engine.getCacher(session.Statement.RefTable.Type) cacher := session.Engine.getCacher2(session.Statement.RefTable)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
ids, err := core.GetCacheSql(cacher, tableName, newsql, args) ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
if err != nil { if err != nil {
@ -3137,7 +3155,8 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
table := session.Engine.autoMap(bean) table := session.Engine.autoMap(bean)
session.Statement.RefTable = table session.Statement.RefTable = table
colNames, args := buildConditions(session.Engine, table, bean, true, true, 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 = "" var condition = ""
@ -3167,7 +3186,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
args = append(session.Statement.Params, args...) 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...) session.cacheDelete(sqlStr, args...)
} }

View File

@ -1,15 +1,13 @@
package xorm package xorm
import ( import (
//"bytes" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
//"strconv"
"encoding/json"
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type inParam struct { type inParam struct {
@ -32,6 +30,7 @@ type Statement struct {
HavingStr string HavingStr string
ColumnStr string ColumnStr string
columnMap map[string]bool columnMap map[string]bool
useAllCols bool
OmitStr string OmitStr string
ConditionStr string ConditionStr string
AltTableName string AltTableName string
@ -47,7 +46,7 @@ type Statement struct {
IsDistinct bool IsDistinct bool
allUseBool bool allUseBool bool
checkVersion bool checkVersion bool
boolColumnMap map[string]bool mustColumnMap map[string]bool
inColumns map[string]*inParam inColumns map[string]*inParam
} }
@ -68,6 +67,7 @@ func (statement *Statement) Init() {
statement.columnMap = make(map[string]bool) statement.columnMap = make(map[string]bool)
statement.ConditionStr = "" statement.ConditionStr = ""
statement.AltTableName = "" statement.AltTableName = ""
statement.IdParam = nil
statement.RawSQL = "" statement.RawSQL = ""
statement.RawParams = make([]interface{}, 0) statement.RawParams = make([]interface{}, 0)
statement.BeanArgs = make([]interface{}, 0) statement.BeanArgs = make([]interface{}, 0)
@ -75,7 +75,7 @@ func (statement *Statement) Init() {
statement.UseAutoTime = true statement.UseAutoTime = true
statement.IsDistinct = false statement.IsDistinct = false
statement.allUseBool = false statement.allUseBool = false
statement.boolColumnMap = make(map[string]bool) statement.mustColumnMap = make(map[string]bool)
statement.checkVersion = true statement.checkVersion = true
statement.inColumns = make(map[string]*inParam) statement.inColumns = make(map[string]*inParam)
} }
@ -118,11 +118,12 @@ func (statement *Statement) Or(querystring string, args ...interface{}) *Stateme
// tempororily set table name // tempororily set table name
func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
t := rType(tableNameOrBean) v := rValue(tableNameOrBean)
t := v.Type()
if t.Kind() == reflect.String { if t.Kind() == reflect.String {
statement.AltTableName = tableNameOrBean.(string) statement.AltTableName = tableNameOrBean.(string)
} else if t.Kind() == reflect.Struct { } else if t.Kind() == reflect.Struct {
statement.RefTable = statement.Engine.autoMapType(t) statement.RefTable = statement.Engine.autoMapType(v)
} }
return statement return statement
} }
@ -245,8 +246,9 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
// Auto generating conditions according a struct // Auto generating conditions according a struct
func buildConditions(engine *Engine, table *core.Table, bean interface{}, func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, includeVersion bool, includeUpdated bool, includeNil bool,
boolColumnMap map[string]bool) ([]string, []interface{}) { includeAutoIncr bool, allUseBool bool, useAllCols bool,
mustColumnMap map[string]bool) ([]string, []interface{}) {
colNames := make([]string, 0) colNames := make([]string, 0)
var args = make([]interface{}, 0) var args = make([]interface{}, 0)
@ -274,7 +276,15 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
fieldValue := *fieldValuePtr fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface()) 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 fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() { if fieldValue.IsNil() {
if includeNil { if includeNil {
@ -297,8 +307,6 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
case reflect.Bool: case reflect.Bool:
if allUseBool || requiredField { if allUseBool || requiredField {
val = fieldValue.Interface() val = fieldValue.Interface()
} else if _, ok := boolColumnMap[col.Name]; ok {
val = fieldValue.Interface()
} else { } else {
// if a bool in a struct, it will not be as a condition because it default is false, // if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead // please use Where() instead
@ -346,7 +354,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
val = t val = t
} }
} else { } else {
engine.autoMapType(fieldValue.Type()) engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok { if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 { if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
@ -534,13 +542,34 @@ func (statement *Statement) Cols(columns ...string) *Statement {
return 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 // indicates that use bool fields as update contents and query contiditions
func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) UseBool(columns ...string) *Statement {
if len(columns) > 0 { if len(columns) > 0 {
newColumns := col2NewCols(columns...) statement.MustCols(columns...)
for _, nc := range newColumns {
statement.boolColumnMap[strings.ToLower(nc)] = true
}
} else { } else {
statement.allUseBool = true statement.allUseBool = true
} }
@ -676,13 +705,7 @@ func (s *Statement) genDelIndexSQL() []string {
} }
func (s *Statement) genDropSQL() string { func (s *Statement) genDropSQL() string {
if s.Engine.dialect.DBType() == core.MSSQL { return s.Engine.dialect.DropTableSql(s.TableName()) + ";"
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()) + ";"
}
} }
func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
@ -690,7 +713,8 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
statement.RefTable = table statement.RefTable = table
colNames, args := buildConditions(statement.Engine, table, bean, true, true, 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.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args statement.BeanArgs = args
@ -729,7 +753,7 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
statement.RefTable = table statement.RefTable = table
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, 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.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args statement.BeanArgs = args

View File

@ -271,7 +271,7 @@ func insertTwoTable(engine *xorm.Engine, t *testing.T) {
} }
type Article struct { type Article struct {
Id int32 `xorm:"pk INT autoincr` Id int32 `xorm:"pk INT autoincr"`
Name string `xorm:"VARCHAR(45)"` Name string `xorm:"VARCHAR(45)"`
Img string `xorm:"VARCHAR(100)"` Img string `xorm:"VARCHAR(100)"`
Aside string `xorm:"VARCHAR(200)"` Aside string `xorm:"VARCHAR(200)"`
@ -334,31 +334,143 @@ func update(engine *xorm.Engine, t *testing.T) {
panic(err) 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 { if err != nil {
t.Error(err) t.Error(err)
panic(err) panic(err)
} }
if cnt != 1 { 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) t.Error(err)
panic(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 { if err != nil {
t.Error(err) t.Error(err)
panic(err) panic(err)
} }
if cnt != 1 { 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) t.Error(err)
panic(err) panic(err)
return 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) { 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) { func in(engine *xorm.Engine, t *testing.T) {
users := make([]Userinfo, 0) 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 { if err != nil {
t.Error(err) t.Error(err)
panic(err) panic(err)
} }
fmt.Println(users) fmt.Println(users)
ids := []interface{}{1, 2, 3} if len(users) != 3 {
err = engine.Where("(id) > ?", 2).In("(id)", ids...).Find(&users) err = errors.New("in uses should be 7,8,9 total 3")
if err != nil {
t.Error(err) t.Error(err)
panic(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") department := engine.ColumnMapper.Obj2Table("Departname")
dev := engine.ColumnMapper.Obj2Table("Dev") dev := engine.ColumnMapper.Obj2Table("Dev")
err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
panic(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 { type User struct {
UserId string `xorm:"varchar(19) not null pk"` UserId string `xorm:"varchar(19) not null pk"`
NickName string `xorm:"varchar(19) not null"` NickName string `xorm:"varchar(19) not null"`
@ -3779,8 +3953,8 @@ type User struct {
} }
func testCompositeKey2(engine *xorm.Engine, t *testing.T) { func testCompositeKey2(engine *xorm.Engine, t *testing.T) {
err := engine.DropTables(&User{}) err := engine.DropTables(&User{})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
panic(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 --------------") fmt.Println("-------------- directCreateTable --------------")
directCreateTable(engine, t) directCreateTable(engine, t)
fmt.Println("-------------- insert --------------") fmt.Println("-------------- insert --------------")
@ -3924,10 +4120,14 @@ func testAll2(engine *xorm.Engine, t *testing.T) {
testPrefixTableName(engine, t) testPrefixTableName(engine, t)
fmt.Println("-------------- testCreatedUpdated --------------") fmt.Println("-------------- testCreatedUpdated --------------")
testCreatedUpdated(engine, t) testCreatedUpdated(engine, t)
fmt.Println("-------------- testLowercase ---------------")
testLowerCase(engine, t)
fmt.Println("-------------- processors --------------") fmt.Println("-------------- processors --------------")
testProcessors(engine, t) testProcessors(engine, t)
fmt.Println("-------------- transaction --------------") fmt.Println("-------------- transaction --------------")
transaction(engine, t) transaction(engine, t)
fmt.Println("-------------- testCustomTableName --------------")
testCustomTableName(engine, t)
} }
// !nash! the 3rd set of the test is intended for non-cache enabled engine // !nash! the 3rd set of the test is intended for non-cache enabled engine

11
xorm.go
View File

@ -9,10 +9,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/lunny/xorm/caches" "github.com/go-xorm/core"
"github.com/lunny/xorm/core" "github.com/go-xorm/xorm/caches"
_ "github.com/lunny/xorm/dialects" _ "github.com/go-xorm/xorm/dialects"
_ "github.com/lunny/xorm/drivers" _ "github.com/go-xorm/xorm/drivers"
) )
const ( const (
@ -50,13 +50,14 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
DriverName: driverName, DriverName: driverName,
DataSourceName: dataSourceName, DataSourceName: dataSourceName,
dialect: dialect, dialect: dialect,
tableCachers: make(map[reflect.Type]core.Cacher)} }
engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
engine.Filters = dialect.Filters() engine.Filters = dialect.Filters()
engine.Tables = make(map[reflect.Type]*core.Table) engine.Tables = make(map[reflect.Type]*core.Table)
engine.mutex = &sync.RWMutex{} engine.mutex = &sync.RWMutex{}
engine.TagIdentifier = "xorm" engine.TagIdentifier = "xorm"

View File

@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"text/template" "text/template"
"github.com/lunny/xorm/core" "github.com/lunny/xorm/core"
) )
@ -238,7 +239,7 @@ func tag(table *core.Table, col *core.Column) string {
nstr := col.SQLType.Name nstr := col.SQLType.Name
if col.Length != 0 { if col.Length != 0 {
if col.Length2 != 0 { if col.Length2 != 0 {
nstr += fmt.Sprintf("(%v, %v)", col.Length, col.Length2) nstr += fmt.Sprintf("(%v,%v)", col.Length, col.Length2)
} else { } else {
nstr += fmt.Sprintf("(%v)", col.Length) nstr += fmt.Sprintf("(%v)", col.Length)
} }

View File

@ -8,6 +8,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" //[SWH|+]
"text/template" "text/template"
_ "github.com/bylevel/pq" _ "github.com/bylevel/pq"
@ -15,6 +16,7 @@ import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/lunny/xorm"
"github.com/lunny/xorm/core" "github.com/lunny/xorm/core"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
_ "github.com/ziutek/mymysql/godrv" _ "github.com/ziutek/mymysql/godrv"
) )
@ -93,12 +95,14 @@ func runReverse(cmd *Command, args []string) {
var genDir string var genDir string
var model string var model string
if len(args) == 4 { if len(args) == 4 {
genDir, err = filepath.Abs(args[3]) genDir, err = filepath.Abs(args[3])
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
//[SWH|+] 经测试path.Base不能解析windows下的“\”,需要替换为“/”
genDir = strings.Replace(genDir, "\\", "/", -1)
model = path.Base(genDir) model = path.Base(genDir)
} else { } else {
model = "model" model = "model"
@ -119,6 +123,7 @@ func runReverse(cmd *Command, args []string) {
var langTmpl LangTmpl var langTmpl LangTmpl
var ok bool var ok bool
var lang string = "go" var lang string = "go"
var prefix string = "" //[SWH|+]
cfgPath := path.Join(dir, "config") cfgPath := path.Join(dir, "config")
info, err := os.Stat(cfgPath) info, err := os.Stat(cfgPath)
@ -131,6 +136,11 @@ func runReverse(cmd *Command, args []string) {
if j, ok := configs["genJson"]; ok { if j, ok := configs["genJson"]; ok {
genJson, err = strconv.ParseBool(j) genJson, err = strconv.ParseBool(j)
} }
//[SWH|+]
if j, ok := configs["prefix"]; ok {
prefix = j
}
} }
if langTmpl, ok = langTmpls[lang]; !ok { if langTmpl, ok = langTmpls[lang]; !ok {
@ -192,6 +202,10 @@ func runReverse(cmd *Command, args []string) {
tbls := make([]*core.Table, 0) tbls := make([]*core.Table, 0)
for _, table := range tables { for _, table := range tables {
//[SWH|+]
if prefix != "" {
table.Name = strings.TrimPrefix(table.Name, prefix)
}
tbls = append(tbls, table) tbls = append(tbls, table)
} }
@ -224,6 +238,10 @@ func runReverse(cmd *Command, args []string) {
w.Close() w.Close()
} else { } else {
for _, table := range tables { for _, table := range tables {
//[SWH|+]
if prefix != "" {
table.Name = strings.TrimPrefix(table.Name, prefix)
}
// imports // imports
tbs := []*core.Table{table} tbs := []*core.Table{table}
imports := langTmpl.GenImports(tbs) imports := langTmpl.GenImports(tbs)

View File

@ -1,2 +1,3 @@
lang=go lang=go
genJson=0 genJson=0
prefix=cos_