Merge branch 'master' of github.com:go-xorm/xorm

This commit is contained in:
Nash Tsai 2014-05-05 17:20:33 +08:00
commit c2ec7bc287
10 changed files with 374 additions and 73 deletions

View File

@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go.
* Query Cache speed up
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md)
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
* Simple cascade loading support
@ -123,13 +123,10 @@ Or
Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm)
# Contributors
# Contributing
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
# LICENSE
BSD License

View File

@ -124,13 +124,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
请加入QQ群280360085 进行讨论。
# 贡献
## 贡献
如果您也想为Xorm贡献您的力量请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai)
## LICENSE
BSD License

View File

@ -58,7 +58,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()
```
Generally, you can only create one engine. Engine supports run on go rutines.
You can create many engines for different databases.Generally, you just need create only one engine. Engine supports run on go routines.
xorm supports four drivers now:
@ -385,7 +385,7 @@ engine.Cols("age", "name").Update(&user)
* Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
```Go
engine.Cols("age").Update(&user)
engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ?
```

View File

@ -23,7 +23,7 @@ xorm 快速入门
* [5.6.Count方法](#66)
* [5.7.Rows方法](#67)
* [6.更新数据](#70)
* [6.1.乐观锁](#71)
* [6.1.乐观锁](#71)
* [7.删除数据](#80)
* [8.执行SQL查询](#90)
* [9.执行SQL命令](#100)
@ -62,7 +62,7 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close()
```
一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
你可以创建一个或多个engine, 不过一般如果操作一个数据库只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
xorm当前支持五种驱动四个数据库如下
@ -419,7 +419,7 @@ engine.Cols("age", "name").Update(&user)
* Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
```Go
engine.Cols("age").Update(&user)
engine.Omit("age").Update(&user)
// UPDATE user SET name = ? AND department = ?
```

106
engine.go
View File

@ -6,6 +6,7 @@ import (
"database/sql"
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
@ -34,8 +35,7 @@ type Engine struct {
ShowErr bool
ShowDebug bool
ShowWarn bool
//Pool IConnectPool
//Filters []core.Filter
Logger ILogger // io.Writer
TZLocation *time.Location
}
@ -266,6 +266,77 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) {
return tables, nil
}
func (engine *Engine) DumpAllToFile(fp string) error {
f, err := os.Create(fp)
if err != nil {
return err
}
defer f.Close()
return engine.DumpAll(f)
}
func (engine *Engine) DumpAll(w io.Writer) error {
tables, err := engine.DBMetas()
if err != nil {
return err
}
for _, table := range tables {
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", "", "")+"\n\n")
if err != nil {
return err
}
for _, index := range table.Indexes {
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n")
if err != nil {
return err
}
}
rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name))
if err != nil {
return err
}
cols, err := rows.Columns()
if err != nil {
return err
}
if len(cols) == 0 {
continue
}
for rows.Next() {
dest := make([]interface{}, len(cols))
err = rows.ScanSlice(&dest)
if err != nil {
return err
}
_, err = io.WriteString(w, "INSERT INTO "+engine.Quote(table.Name)+" ("+engine.Quote(strings.Join(cols, engine.Quote(", ")))+") VALUES (")
if err != nil {
return err
}
var temp string
for _, d := range dest {
if d == nil {
temp += ", NULL"
} else if reflect.TypeOf(d).Kind() == reflect.String {
temp += ", '" + strings.Replace(d.(string), "'", "''", -1) + "'"
} else if reflect.TypeOf(d).Kind() == reflect.Slice {
temp += fmt.Sprintf(", %s", engine.dialect.FormatBytes(d.([]byte)))
} else {
temp += fmt.Sprintf(", %v", d)
}
}
_, err = io.WriteString(w, temp[2:]+");\n\n")
if err != nil {
return err
}
}
}
return nil
}
// use cascade or not
func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
session := engine.NewSession()
@ -456,15 +527,6 @@ func (engine *Engine) autoMap(bean interface{}) *core.Table {
return engine.autoMapType(v)
}
/*func (engine *Engine) mapType(t reflect.Type) *core.Table {
return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier)
}*/
/*
func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
table := core.NewEmptyTable()
table.Name = tableMapper.Obj2Table(t.Name())
*/
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
if index, ok := table.Indexes[indexName]; ok {
index.AddColumn(col.Name)
@ -524,17 +586,19 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if tags[0] == "-" {
continue
}
if (strings.ToUpper(tags[0]) == "EXTENDS") &&
(fieldType.Kind() == reflect.Struct) {
if strings.ToUpper(tags[0]) == "EXTENDS" {
fieldValue = reflect.Indirect(fieldValue)
if fieldValue.Kind() == reflect.Struct {
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
parentTable := engine.mapType(fieldValue)
for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldValue.Type().Name(), col.FieldName)
table.AddColumn(col)
}
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
parentTable := engine.mapType(fieldValue)
for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
table.AddColumn(col)
continue
}
continue
//TODO: warning
}
indexNames := make(map[string]int)
@ -701,7 +765,7 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
session := engine.NewSession()
defer session.Close()
rows, err := session.Count(bean)
return rows > 0, err
return rows == 0, err
}
// If a table is exist

34
examples/tables.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"os"
"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("need db path")
return
}
orm, err := xorm.NewEngine("sqlite3", os.Args[1])
if err != nil {
fmt.Println(err)
return
}
defer orm.Close()
orm.ShowSQL = true
tables, err := orm.DBMetas()
if err != nil {
fmt.Println(err)
return
}
for _, table := range tables {
fmt.Println(table.Name)
}
}

View File

@ -15,6 +15,10 @@ type BeforeDeleteProcessor interface {
BeforeDelete()
}
type BeforeSetProcessor interface {
BeforeSet(string, Cell)
}
// !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations
//// Executed before an object is validated
//type BeforeValidateProcessor interface {

View File

@ -883,7 +883,6 @@ func (session *Session) Rows(bean interface{}) (*Rows, error) {
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
rows, err := session.Rows(bean)
if err != nil {
return err
@ -982,24 +981,6 @@ func (session *Session) Get(bean interface{}) (bool, error) {
} else {
return false, nil
}
// resultsSlice, err := session.query(sqlStr, args...)
// if err != nil {
// return false, err
// }
// if len(resultsSlice) < 1 {
// return false, nil
// }
// err = session.scanMapIntoStruct(bean, resultsSlice[0])
// if err != nil {
// return true, err
// }
// if len(resultsSlice) == 1 {
// return true, nil
// } else {
// return true, errors.New("More than one record")
// }
}
// Count counts the records. bean's non-empty fields
@ -1441,6 +1422,8 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
return fieldValue
}
type Cell *interface{}
func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
dataStruct := rValue(bean)
if dataStruct.Kind() != reflect.Struct {
@ -1449,18 +1432,24 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
table := session.Engine.autoMapType(dataStruct)
scanResultContainers := make([]interface{}, len(fields))
scanResults := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ {
var scanResultContainer interface{}
scanResultContainers[i] = &scanResultContainer
var cell interface{}
scanResults[i] = &cell
}
if err := rows.Scan(scanResultContainers...); err != nil {
if err := rows.Scan(scanResults...); err != nil {
return err
}
b, hasBeforeSet := bean.(BeforeSetProcessor)
for ii, key := range fields {
if hasBeforeSet {
b.BeforeSet(fields[ii], Cell(scanResults[ii].(*interface{})))
}
if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil {
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
//if row is null then ignore
if rawValue.Interface() == nil {
@ -1468,7 +1457,18 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
continue
}
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data)
} else {
session.Engine.LogError(err)
}
continue
}
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
if data, err := value2Bytes(&rawValue); err == nil {
structConvert.FromDB(data)
} else {
@ -2451,6 +2451,16 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
}
}
}
if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := fieldConvert.ToDB()
if err != nil {
return 0, err
} else {
return string(data), nil
}
}
fieldType := fieldValue.Type()
k := fieldType.Kind()
if k == reflect.Ptr {
@ -2470,11 +2480,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
switch k {
case reflect.Bool:
return fieldValue.Bool(), nil
/*if fieldValue.Bool() {
return 1, nil
} else {
return 0, nil
}*/
case reflect.String:
return fieldValue.String(), nil
case reflect.Struct:
@ -2947,7 +2952,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
session.Statement.RefTable = table
if session.Statement.ColumnStr == "" {
colNames, args = buildConditions(session.Engine, table, bean, false, false,
colNames, args = buildUpdates(session.Engine, table, bean, false, false,
false, false, session.Statement.allUseBool, session.Statement.useAllCols,
session.Statement.mustColumnMap)
} else {
@ -3314,7 +3319,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool,
}
if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
args = append(args, time.Now())
args = append(args, session.Engine.NowTime(col.SQLType.Name))
} else if col.IsVersion && session.Statement.checkVersion {
args = append(args, 1)
} else {

View File

@ -1,6 +1,7 @@
package xorm
import (
"fmt"
"strings"
"github.com/go-xorm/core"
@ -44,6 +45,10 @@ func (db *sqlite3) SqlType(c *core.Column) string {
}
}
func (db *sqlite3) FormatBytes(bs []byte) string {
return fmt.Sprintf("X'%x'", bs)
}
func (db *sqlite3) SupportInsertMany() bool {
return true
}

View File

@ -253,6 +253,197 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return results
}*/
// Auto generating conditions according a struct
func buildUpdates(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool,
mustColumnMap map[string]bool) ([]string, []interface{}) {
colNames := make([]string, 0)
var args = make([]interface{}, 0)
for _, col := range table.Columns() {
if !includeVersion && col.IsVersion {
continue
}
if col.IsCreated {
continue
}
if !includeUpdated && col.IsUpdated {
continue
}
if !includeAutoIncr && col.IsAutoIncrement {
continue
}
//
//fmt.Println(engine.dialect.DBType(), Text)
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean)
if err != nil {
engine.LogError(err)
continue
}
fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b {
requiredField = true
} else {
continue
}
}
var val interface{}
if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
}
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
data, err := structConvert.ToDB()
if err != nil {
engine.LogError(err)
} else {
val = data
}
continue
}
if fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
if includeNil {
args = append(args, nil)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
continue
} else if !fieldValue.IsValid() {
continue
} else {
// dereference ptr type to instance type
fieldValue = fieldValue.Elem()
fieldType = reflect.TypeOf(fieldValue.Interface())
requiredField = true
}
}
switch fieldType.Kind() {
case reflect.Bool:
if allUseBool || requiredField {
val = fieldValue.Interface()
} else {
// if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead
continue
}
case reflect.String:
if !requiredField && fieldValue.String() == "" {
continue
}
// for MyString, should convert to string or panic
if fieldType.String() != reflect.String.String() {
val = fieldValue.String()
} else {
val = fieldValue.Interface()
}
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
if !requiredField && fieldValue.Int() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Float32, reflect.Float64:
if !requiredField && fieldValue.Float() == 0.0 {
continue
}
val = fieldValue.Interface()
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
if !requiredField && fieldValue.Uint() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Struct:
if fieldType == reflect.TypeOf(time.Now()) {
t := fieldValue.Interface().(time.Time)
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
continue
}
val = engine.FormatTime(col.SQLType.Name, t)
//fmt.Println("-------", t, val, col.Name)
} else {
engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
if pkField.Int() != 0 {
val = pkField.Interface()
} else {
continue
}
} else {
//TODO: how to handler?
}
} else {
val = fieldValue.Interface()
}
}
case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = string(bytes)
} else if col.SQLType.IsBlob() {
var bytes []byte
var err error
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
fieldType.Elem().Kind() == reflect.Uint8 {
if fieldValue.Len() > 0 {
val = fieldValue.Bytes()
} else {
continue
}
} else {
bytes, err = json.Marshal(fieldValue.Interface())
if err != nil {
engine.LogError(err)
continue
}
val = bytes
}
} else {
continue
}
default:
val = fieldValue.Interface()
}
args = append(args, val)
colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
}
return colNames, args
}
// Auto generating conditions according a struct
func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
@ -283,8 +474,11 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
}
fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface())
if fieldValue.Interface() == nil {
continue
}
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
if b {
@ -353,7 +547,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
continue
}
val = engine.FormatTime(col.SQLType.Name, t)
fmt.Println("-------", t, val, col.Name)
//fmt.Println("-------", t, val, col.Name)
} else {
engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
@ -762,11 +956,12 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
statement.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args
// count(index fieldname) > count(0) > count(*)
var id string = "0"
// for compitable on kinds of database, just use *
/*var id string = "0"
if len(table.PrimaryKeys) == 1 {
id = statement.Engine.Quote(table.PrimaryKeys[0])
}
return statement.genSelectSql(fmt.Sprintf("COUNT(%v) AS %v", id, statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...)
id = statement.Engine.Quote(statement.TableName()) + "." + statement.Engine.Quote(table.PrimaryKeys[0])
}*/
return statement.genSelectSql(fmt.Sprintf("COUNT(*) AS %v", statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...)
}
func (statement *Statement) genSelectSql(columnStr string) (a string) {