diff --git a/README.md b/README.md index 7dcdf0c0..ecdfa721 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -[中文](https://github.com/lunny/xorm/blob/master/README_CN.md) - -Xorm is a simple and powerful ORM for Go. - +[中文](https://github.com/lunny/xorm/blob/master/README_CN.md) + +Xorm is a simple and powerful ORM for Go. + [![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") -# Features - +# Features + * Struct <-> Table Mapping Support * Chainable APIs * Transaction Support - + * Both ORM and raw SQL operation Support * Sync database sechmea Support @@ -24,26 +24,26 @@ Xorm is a simple and powerful ORM for Go. * Optimistic Locking support - -# Drivers Support - -Drivers for Go's sql package which currently support database/sql includes: - + +# Drivers Support + +Drivers for Go's sql package which currently support database/sql includes: + * 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) - -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - -* Postgres: [github.com/lib/pq](https://github.com/lib/pq) - -* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) - + +* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) + +* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) + +* Postgres: [github.com/lib/pq](https://github.com/lib/pq) + +* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) + # Changelog -* **v0.3.1** - - Features: +* **v0.3.1** + + Features: * Support MSSQL DB via ODBC driver ([github.com/lunny/godbc](https://github.com/lunny/godbc)); * Composite Key, using multiple pk xorm tag * Added Row() API as alternative to Iterate() API for traversing result set, provide similar usages to sql.Rows type @@ -54,22 +54,22 @@ Drivers for Go's sql package which currently support database/sql includes: * Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type * Performance improvement for Get()/Find()/Iterate() -[More changelogs ...](https://github.com/lunny/xorm/blob/master/docs/Changelog.md) - +[More changelogs ...](https://github.com/lunny/xorm/blob/master/docs/Changelog.md) + # Installation If you have [gopm](https://github.com/gpmgo/gopm) installed, gopm get github.com/lunny/xorm -Or - - go get github.com/lunny/xorm - +Or + + go get github.com/lunny/xorm + # Documents -* [GoDoc](http://godoc.org/github.com/lunny/xorm) - +* [GoDoc](http://godoc.org/github.com/lunny/xorm) + * [GoWalker](http://gowalker.org/github.com/lunny/xorm) * [Quick Start](https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md) @@ -82,12 +82,12 @@ Or * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily) -* [Very Hour](http://veryhour.com/) - -# Todo - -[Todo List](https://trello.com/b/IHsuAnhk/xorm) - +* [Very Hour](http://veryhour.com/) + +# Todo + +[Todo List](https://trello.com/b/IHsuAnhk/xorm) + # Discuss Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) @@ -97,9 +97,9 @@ Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xor If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md) * [Lunny](https://github.com/lunny) -* [Nashtsai](https://github.com/nashtsai) - -# LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) +* [Nashtsai](https://github.com/nashtsai) + +# LICENSE + + BSD License + [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/README_CN.md b/README_CN.md index 8399b87f..d1b88e67 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,7 +1,7 @@ # xorm - -[English](https://github.com/lunny/xorm/blob/master/README.md) - + +[English](https://github.com/lunny/xorm/blob/master/README.md) + xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 [![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) @@ -27,11 +27,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * 支持记录版本(即乐观锁) ## 驱动支持 - -目前支持的Go数据库驱动和对应的数据库如下: - -* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) - + +目前支持的Go数据库驱动和对应的数据库如下: + +* 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) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) @@ -42,7 +42,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 更新日志 -* **v0.3.1** +* **v0.3.1** 新特性: * 支持 MSSQL DB 通过 ODBC 驱动 ([github.com/lunny/godbc](https://github.com/lunny/godbc)); @@ -53,11 +53,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 改进: * 允许 int/int32/int64/uint/uint32/uint64/string 作为主键类型 - * 查询函数 Get()/Find()/Iterate() 在性能上的改进 + * 查询函数 Get()/Find()/Iterate() 在性能上的改进 [更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md) - + ## 安装 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: @@ -66,10 +66,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 或者您也可以使用go工具进行安装: - go get github.com/lunny/xorm - + go get github.com/lunny/xorm + ## 文档 - + * [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md) * [GoWalker代码文档](http://gowalker.org/github.com/lunny/xorm) @@ -85,8 +85,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily) -* [Very Hour](http://veryhour.com/) - +* [Very Hour](http://veryhour.com/) + ## Todo [开发计划](https://trello.com/b/IHsuAnhk/xorm) @@ -101,8 +101,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * [Lunny](https://github.com/lunny) * [Nashtsai](https://github.com/nashtsai) - -## LICENSE - -BSD License -[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) + +## LICENSE + +BSD License +[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/engine.go b/engine.go index b5fd317f..1e303696 100644 --- a/engine.go +++ b/engine.go @@ -590,7 +590,7 @@ func (engine *Engine) mapType(t reflect.Type) *Table { sqlType := Type2SQLType(fieldType) col = &Column{engine.columnMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, sqlType.DefaultLength, sqlType.DefaultLength2, true, "", make(map[string]bool), false, false, - TWOSIDES, false, false, false, false} + TWOSIDES, false, false, false, false, nil} } if col.IsAutoIncrement { col.Nullable = false diff --git a/oracle.go b/oracle.go index b863b6ab..4e3c6fb6 100644 --- a/oracle.go +++ b/oracle.go @@ -1,258 +1,258 @@ -package xorm - -import ( - "database/sql" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -type oracle struct { - base -} - -type oracleParser struct { -} - -//dataSourceName=user/password@ipv4:port/dbname -//dataSourceName=user/password@[ipv6]:port/dbname -func (p *oracleParser) parse(driverName, dataSourceName string) (*uri, error) { - db := &uri{dbType: ORACLE_OCI} - dsnPattern := regexp.MustCompile( - `^(?P.*)\/(?P.*)@` + // user:password@ - `(?P.*)` + // ip:port - `\/(?P.*)`) // dbname - matches := dsnPattern.FindStringSubmatch(dataSourceName) - names := dsnPattern.SubexpNames() - for i, match := range matches { - switch names[i] { - case "dbname": - db.dbName = match - } - } - if db.dbName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} - -func (db *oracle) Init(drivername, uri string) error { - return db.base.init(&oracleParser{}, drivername, uri) -} - -func (db *oracle) SqlType(c *Column) string { - var res string - switch t := c.SQLType.Name; t { - case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: - return "NUMBER" - case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: - return Blob - case Time, DateTime, TimeStamp: - res = TimeStamp - case TimeStampz: - res = "TIMESTAMP WITH TIME ZONE" - case Float, Double, Numeric, Decimal: - res = "NUMBER" - case Text, MediumText, LongText: - res = "CLOB" - case Char, Varchar, TinyText: - return "VARCHAR2" - default: - res = t - } - - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) - if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" - } else if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" - } - return res -} - -func (db *oracle) SupportInsertMany() bool { - return true -} - -func (db *oracle) QuoteStr() string { - return "\"" -} - -func (db *oracle) AutoIncrStr() string { - return "" -} - -func (db *oracle) SupportEngine() bool { - return false -} - -func (db *oracle) SupportCharset() bool { - return false -} - -func (db *oracle) IndexOnTable() bool { - return false -} - -func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} - return `SELECT INDEX_NAME FROM USER_INDEXES ` + - `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args -} - -func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName)} - return `SELECT table_name FROM user_tables WHERE table_name = ?`, args -} - -func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)} - return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + - " AND column_name = ?", args -} - -func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) { - args := []interface{}{strings.ToUpper(tableName)} - s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + - "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" - - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, nil, err - } - cols := make(map[string]*Column) - colSeq := make([]string, 0) - for _, record := range res { - col := new(Column) - col.Indexes = make(map[string]bool) - for name, content := range record { - switch name { - case "column_name": - col.Name = strings.Trim(string(content), `" `) - case "data_default": - col.Default = string(content) - case "nullable": - if string(content) == "Y" { - col.Nullable = true - } else { - col.Nullable = false - } - case "data_type": - ct := string(content) - switch ct { - case "VARCHAR2": - col.SQLType = SQLType{Varchar, 0, 0} - case "TIMESTAMP WITH TIME ZONE": - col.SQLType = SQLType{TimeStamp, 0, 0} - default: - col.SQLType = SQLType{strings.ToUpper(ct), 0, 0} - } - if _, ok := sqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct)) - } - case "data_length": - i, err := strconv.Atoi(string(content)) - if err != nil { - return nil, nil, errors.New("retrieve length error") - } - col.Length = i - case "data_precision": - case "data_scale": - } - } - if col.SQLType.IsText() { - if col.Default != "" { - col.Default = "'" + col.Default + "'" - } - } - cols[col.Name] = col - colSeq = append(colSeq, col.Name) - } - - return colSeq, cols, nil -} - -func (db *oracle) GetTables() ([]*Table, error) { - args := []interface{}{} - s := "SELECT table_name FROM user_tables" - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, err - } - - tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "table_name": - table.Name = string(content) - } - } - tables = append(tables, table) - } - return tables, nil -} - -func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { - args := []interface{}{tableName} - s := "SELECT t.column_name,i.table_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + - "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1" - - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, err - } - - indexes := make(map[string]*Index, 0) - for _, record := range res { - var indexType int - var indexName string - var colName string - - for name, content := range record { - switch name { - case "index_name": - indexName = strings.Trim(string(content), `" `) - case "uniqueness": - c := string(content) - if c == "UNIQUE" { - indexType = UniqueType - } else { - indexType = IndexType - } - case "column_name": - colName = string(content) - } - } - - var index *Index - var ok bool - if index, ok = indexes[indexName]; !ok { - index = new(Index) - index.Type = indexType - index.Name = indexName - indexes[indexName] = index - } - index.AddColumn(colName) - } - return indexes, nil -} +package xorm + +import ( + "database/sql" + "errors" + "fmt" + "regexp" + "strconv" + "strings" +) + +type oracle struct { + base +} + +type oracleParser struct { +} + +//dataSourceName=user/password@ipv4:port/dbname +//dataSourceName=user/password@[ipv6]:port/dbname +func (p *oracleParser) parse(driverName, dataSourceName string) (*uri, error) { + db := &uri{dbType: ORACLE_OCI} + dsnPattern := regexp.MustCompile( + `^(?P.*)\/(?P.*)@` + // user:password@ + `(?P.*)` + // ip:port + `\/(?P.*)`) // dbname + matches := dsnPattern.FindStringSubmatch(dataSourceName) + names := dsnPattern.SubexpNames() + for i, match := range matches { + switch names[i] { + case "dbname": + db.dbName = match + } + } + if db.dbName == "" { + return nil, errors.New("dbname is empty") + } + return db, nil +} + +func (db *oracle) Init(drivername, uri string) error { + return db.base.init(&oracleParser{}, drivername, uri) +} + +func (db *oracle) SqlType(c *Column) string { + var res string + switch t := c.SQLType.Name; t { + case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: + return "NUMBER" + case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: + return Blob + case Time, DateTime, TimeStamp: + res = TimeStamp + case TimeStampz: + res = "TIMESTAMP WITH TIME ZONE" + case Float, Double, Numeric, Decimal: + res = "NUMBER" + case Text, MediumText, LongText: + res = "CLOB" + case Char, Varchar, TinyText: + return "VARCHAR2" + default: + res = t + } + + var hasLen1 bool = (c.Length > 0) + var hasLen2 bool = (c.Length2 > 0) + if hasLen1 { + res += "(" + strconv.Itoa(c.Length) + ")" + } else if hasLen2 { + res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + } + return res +} + +func (db *oracle) SupportInsertMany() bool { + return true +} + +func (db *oracle) QuoteStr() string { + return "\"" +} + +func (db *oracle) AutoIncrStr() string { + return "" +} + +func (db *oracle) SupportEngine() bool { + return false +} + +func (db *oracle) SupportCharset() bool { + return false +} + +func (db *oracle) IndexOnTable() bool { + return false +} + +func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} + return `SELECT INDEX_NAME FROM USER_INDEXES ` + + `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args +} + +func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName)} + return `SELECT table_name FROM user_tables WHERE table_name = ?`, args +} + +func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)} + return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + + " AND column_name = ?", args +} + +func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) { + args := []interface{}{strings.ToUpper(tableName)} + s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" + + cnn, err := sql.Open(db.driverName, db.dataSourceName) + if err != nil { + return nil, nil, err + } + defer cnn.Close() + res, err := query(cnn, s, args...) + if err != nil { + return nil, nil, err + } + cols := make(map[string]*Column) + colSeq := make([]string, 0) + for _, record := range res { + col := new(Column) + col.Indexes = make(map[string]bool) + for name, content := range record { + switch name { + case "column_name": + col.Name = strings.Trim(string(content), `" `) + case "data_default": + col.Default = string(content) + case "nullable": + if string(content) == "Y" { + col.Nullable = true + } else { + col.Nullable = false + } + case "data_type": + ct := string(content) + switch ct { + case "VARCHAR2": + col.SQLType = SQLType{Varchar, 0, 0} + case "TIMESTAMP WITH TIME ZONE": + col.SQLType = SQLType{TimeStamp, 0, 0} + default: + col.SQLType = SQLType{strings.ToUpper(ct), 0, 0} + } + if _, ok := sqlTypes[col.SQLType.Name]; !ok { + return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct)) + } + case "data_length": + i, err := strconv.Atoi(string(content)) + if err != nil { + return nil, nil, errors.New("retrieve length error") + } + col.Length = i + case "data_precision": + case "data_scale": + } + } + if col.SQLType.IsText() { + if col.Default != "" { + col.Default = "'" + col.Default + "'" + } + } + cols[col.Name] = col + colSeq = append(colSeq, col.Name) + } + + return colSeq, cols, nil +} + +func (db *oracle) GetTables() ([]*Table, error) { + args := []interface{}{} + s := "SELECT table_name FROM user_tables" + cnn, err := sql.Open(db.driverName, db.dataSourceName) + if err != nil { + return nil, err + } + defer cnn.Close() + res, err := query(cnn, s, args...) + if err != nil { + return nil, err + } + + tables := make([]*Table, 0) + for _, record := range res { + table := new(Table) + for name, content := range record { + switch name { + case "table_name": + table.Name = string(content) + } + } + tables = append(tables, table) + } + return tables, nil +} + +func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { + args := []interface{}{tableName} + s := "SELECT t.column_name,i.table_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + + "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1" + + cnn, err := sql.Open(db.driverName, db.dataSourceName) + if err != nil { + return nil, err + } + defer cnn.Close() + res, err := query(cnn, s, args...) + if err != nil { + return nil, err + } + + indexes := make(map[string]*Index, 0) + for _, record := range res { + var indexType int + var indexName string + var colName string + + for name, content := range record { + switch name { + case "index_name": + indexName = strings.Trim(string(content), `" `) + case "uniqueness": + c := string(content) + if c == "UNIQUE" { + indexType = UniqueType + } else { + indexType = IndexType + } + case "column_name": + colName = string(content) + } + } + + var index *Index + var ok bool + if index, ok = indexes[indexName]; !ok { + index = new(Index) + index.Type = indexType + index.Name = indexName + indexes[indexName] = index + } + index.AddColumn(colName) + } + return indexes, nil +} diff --git a/session.go b/session.go index 7623009b..7d70c16e 100644 --- a/session.go +++ b/session.go @@ -1092,25 +1092,41 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) fieldsCount := len(fields) - for rawRows.Next() { - var newValue reflect.Value + var newElemFunc func() reflect.Value + if sliceElementType.Kind() == reflect.Ptr { + newElemFunc = func() reflect.Value { + return reflect.New(sliceElementType.Elem()) + } + } else { + newElemFunc = func() reflect.Value { + return reflect.New(sliceElementType) + } + } + + var sliceValueSetFunc func(*reflect.Value) + + if sliceValue.Kind() == reflect.Slice { if sliceElementType.Kind() == reflect.Ptr { - newValue = reflect.New(sliceElementType.Elem()) - } else { - newValue = reflect.New(sliceElementType) - } - err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) - if err != nil { - return err - } - if sliceValue.Kind() == reflect.Slice { - if sliceElementType.Kind() == reflect.Ptr { + sliceValueSetFunc = func(newValue *reflect.Value) { sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface()))) - } else { + } + } else { + sliceValueSetFunc = func(newValue *reflect.Value) { sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) } } } + + for rawRows.Next() { + var newValue reflect.Value = newElemFunc() + if sliceValueSetFunc != nil { + err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) + if err != nil { + return err + } + sliceValueSetFunc(&newValue) + } + } } else { resultsSlice, err := session.query(sqlStr, args...) if err != nil { @@ -1347,32 +1363,35 @@ func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err func (session *Session) getField(dataStruct *reflect.Value, key string, table *Table) *reflect.Value { - key = strings.ToLower(key) - if _, ok := table.Columns[key]; !ok { + //key = strings.ToLower(key) + if col, ok := table.Columns[key]; !ok { session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.ColumnsSeq)) return nil - } - col := table.Columns[key] - fieldName := col.FieldName - fieldPath := strings.Split(fieldName, ".") - var fieldValue reflect.Value - if len(fieldPath) > 2 { - session.Engine.LogError("Unsupported mutliderive", fieldName) - return nil - } else if len(fieldPath) == 2 { - parentField := dataStruct.FieldByName(fieldPath[0]) - if parentField.IsValid() { - fieldValue = parentField.FieldByName(fieldPath[1]) - } } else { - fieldValue = dataStruct.FieldByName(fieldName) + fieldName := col.FieldName + if col.fieldPath == nil { + col.fieldPath = strings.Split(fieldName, ".") + } + var fieldValue reflect.Value + fieldPathLen := len(col.fieldPath) + if fieldPathLen > 2 { + session.Engine.LogError("Unsupported mutliderive", fieldName) + return nil + } else if fieldPathLen == 2 { + parentField := dataStruct.FieldByName(col.fieldPath[0]) + if parentField.IsValid() { + fieldValue = parentField.FieldByName(col.fieldPath[1]) + } + } else { + fieldValue = dataStruct.FieldByName(fieldName) + } + if !fieldValue.IsValid() || !fieldValue.CanSet() { + session.Engine.LogWarn("table %v's column %v is not valid or cannot set", + table.Name, key) + return nil + } + return &fieldValue } - if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.Engine.LogWarn("table %v's column %v is not valid or cannot set", - table.Name, key) - return nil - } - return &fieldValue } func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error { @@ -1395,7 +1414,6 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in for ii, key := range fields { if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) //if row is null then ignore diff --git a/table.go b/table.go index a1ae7cdc..0667a12e 100644 --- a/table.go +++ b/table.go @@ -275,6 +275,7 @@ type Column struct { IsUpdated bool IsCascade bool IsVersion bool + fieldPath []string } // generate column description string according dialect diff --git a/xorm/README.md b/xorm/README.md index aebfca77..b0d39b86 100644 --- a/xorm/README.md +++ b/xorm/README.md @@ -1,30 +1,30 @@ -# xorm tools - - -xorm tools is a set of tools for database operation. - -## Install - +# xorm tools + + +xorm tools is a set of tools for database operation. + +## Install + `go get github.com/lunny/xorm/xorm` and you should install the depends below: -* github.com/lunny/xorm - -* 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) - -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - +* github.com/lunny/xorm + +* 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) + +* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) + * Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq) - - -## Reverse + + +## Reverse After you installed the tool, you can type -`xorm help reverse` +`xorm help reverse` to get help @@ -50,13 +50,13 @@ Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three ```` lang=go -genJson=1 +genJson=1 ``` lang must be go or c++ now. genJson can be 1 or 0, if 1 then the struct will have json tag. - -## LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) + +## LICENSE + + BSD License + [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)