Conflicts:
	session.go
This commit is contained in:
hzm 2016-01-04 02:31:21 +08:00
commit 7d79bd259b
10 changed files with 668 additions and 478 deletions

146
README.md
View File

@ -11,7 +11,7 @@ Xorm is a simple and powerful ORM for Go.
* Struct <-> Table Mapping Support
* Chainable APIs
* Transaction Support
* Both ORM and raw SQL operation Support
@ -72,7 +72,7 @@ Drivers for Go's sql package which currently support database/sql includes:
# Installation
If you have [gopm](https://github.com/gpmgo/gopm) installed,
If you have [gopm](https://github.com/gpmgo/gopm) installed,
gopm get github.com/go-xorm/xorm
@ -88,6 +88,148 @@ Or
* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm)
# Quick Start
* Create Engine
```Go
engine, err := xorm.NewEngine(driverName, dataSourceName)
```
* Define a struct and Sync2 table struct to database
```Go
type User struct {
Id int64
Name string
Salt string
Age int
Passwd string `xorm:"varchar(200)"`
Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"`
}
err := engine.Sync2(new(User))
```
* Query a SQL string, the returned results is []map[string][]byte
```Go
results, err := engine.Query("select * from user")
```
* Execute a SQL string, the returned results
```Go
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
```
* Insert one or multipe records to database
```Go
affected, err := engine.Insert(&user)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()
```
* Query one record from database
```Go
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
```
* Query multiple records from database, also you can use join and extends
```Go
var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
type Detail struct {
Id int64
UserId int64 `xorm:"index"`
}
type UserDetail struct {
User `xorm:"extends"`
Detail `xorm:"extends"`
}
var users []UserDetail
err := engine.Table("user").Select("user.*, detail.*")
Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0).
Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
```
* Query multiple records and record by record handle, there two methods Iterate and Rows
```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user
rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user
defer rows.Close()
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}
```
* Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc.
```Go
affected, err := engine.Id(1).Update(&user)
// UPDATE user SET ... Where id = ?
affected, err := engine.Update(&user, &User{Name:name})
// UPDATE user SET ... Where name = ?
var ids = []int64{1, 2, 3}
affected, err := engine.In(ids).Update(&user)
// UPDATE user SET ... Where id IN (?, ?, ?)
// force update indicated columns by Cols
affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
// force NOT update indicated columns by Omit
affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
affected, err := engine.Id(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
```
* Delete one or more records, Delete MUST has conditon
```Go
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
```
* Count records
```Go
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
```
# Cases
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)

View File

@ -20,7 +20,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
* 支持级联加载Struct
* 支持级联加载Struct
* 支持缓存
@ -58,7 +58,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* sql.NullString支持
* ForUpdate 支持
* bug修正
* **v0.4.3**
* Json 字段类型支持
* oracle实验性支持
@ -68,10 +68,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
## 安装
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
gopm get github.com/go-xorm/xorm
或者您也可以使用go工具进行安装
go get github.com/go-xorm/xorm
@ -84,8 +84,149 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
# 快速开始
## 案例
* 第一步创建引擎driverName, dataSourceName和database/sql接口相同
```Go
engine, err := xorm.NewEngine(driverName, dataSourceName)
```
* 定义一个和表同步的结构体,并且自动同步结构体到数据库
```Go
type User struct {
Id int64
Name string
Salt string
Age int
Passwd string `xorm:"varchar(200)"`
Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"`
}
err := engine.Sync2(new(User))
```
* 最原始的也支持SQL语句查询返回的结果类型为 []map[string][]byte
```Go
results, err := engine.Query("select * from user")
```
* 执行一个SQL语句
```Go
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
```
* 插入一条或者多条记录
```Go
affected, err := engine.Insert(&user)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()
```
* 查询单条记录
```Go
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
```
* 查询多条记录当然可以使用Join和extends来组合使用
```Go
var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10
type Detail struct {
Id int64
UserId int64 `xorm:"index"`
}
type UserDetail struct {
User `xorm:"extends"`
Detail `xorm:"extends"`
}
var users []UserDetail
err := engine.Table("user").Select("user.*, detail.*")
Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0).
Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10
```
* 根据条件遍历数据库,可以有两种方式: Iterate and Rows
```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user
rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user
defer rows.Close()
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}
```
* 更新数据除非使用Cols,AllCols函数指明默认只更新非空和非0的字段
```Go
affected, err := engine.Id(1).Update(&user)
// UPDATE user SET ... Where id = ?
affected, err := engine.Update(&user, &User{Name:name})
// UPDATE user SET ... Where name = ?
var ids = []int64{1, 2, 3}
affected, err := engine.In(ids).Update(&user)
// UPDATE user SET ... Where id IN (?, ?, ?)
// force update indicated columns by Cols
affected, err := engine.Id(1).Cols("age").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
// force NOT update indicated columns by Omit
affected, err := engine.Id(1).Omit("name").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
affected, err := engine.Id(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
```
* 删除记录需要注意删除必须至少有一个条件否则会报错。要清空数据库可以用EmptyTable
```Go
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
```
* 获取记录条数
```Go
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
```
# 案例
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)

View File

@ -1 +1 @@
xorm v0.4.4.1029
xorm v0.4.5.0102

View File

@ -320,6 +320,12 @@ func (engine *Engine) NoAutoTime() *Session {
return session.NoAutoTime()
}
func (engine *Engine) NoAutoCondition(no ...bool) *Session {
session := engine.NewSession()
session.IsAutoClose = true
return session.NoAutoCondition(no...)
}
// Retrieve all tables, columns, indexes' informations from database.
func (engine *Engine) DBMetas() ([]*core.Table, error) {
tables, err := engine.dialect.GetTables()
@ -377,13 +383,25 @@ func (engine *Engine) DumpAll(w io.Writer) error {
return err
}
for _, table := range tables {
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+"\n\n")
_, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n",
Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05")))
if err != nil {
return err
}
for i, table := range tables {
if i > 0 {
_, err = io.WriteString(w, "\n")
if err != nil {
return err
}
}
_, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
if err != nil {
return err
}
for _, index := range table.Indexes {
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n")
_, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n")
if err != nil {
return err
}
@ -443,7 +461,7 @@ func (engine *Engine) DumpAll(w io.Writer) error {
}
}
}
_, err = io.WriteString(w, temp[2:]+");\n\n")
_, err = io.WriteString(w, temp[2:]+");\n")
if err != nil {
return err
}
@ -747,7 +765,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)}
tags := strings.Split(ormTagStr, " ")
tags := splitTag(ormTagStr)
if len(tags) > 0 {
if tags[0] == "-" {

View File

@ -15,6 +15,30 @@ import (
"github.com/go-xorm/core"
)
func splitTag(tag string) (tags []string) {
tag = strings.TrimSpace(tag)
var hasQuote = false
var lastIdx = 0
for i, t := range tag {
if t == '\'' {
hasQuote = !hasQuote
} else if t == ' ' {
if lastIdx < i && !hasQuote {
tags = append(tags, strings.TrimSpace(tag[lastIdx:i]))
lastIdx = i + 1
}
}
}
if lastIdx < len(tag) {
tags = append(tags, strings.TrimSpace(tag[lastIdx:len(tag)]))
}
return
}
type zeroable interface {
IsZero() bool
}
func isZero(k interface{}) bool {
switch k.(type) {
case int:
@ -45,12 +69,33 @@ func isZero(k interface{}) bool {
return k.(bool) == false
case string:
return k.(string) == ""
case time.Time:
return k.(time.Time).IsZero()
case zeroable:
return k.(zeroable).IsZero()
}
return false
}
func int64ToInt(id int64, k reflect.Kind) interface{} {
var v interface{} = id
switch k {
case reflect.Int16:
v = int16(id)
case reflect.Int32:
v = int32(id)
case reflect.Int:
v = int(id)
case reflect.Uint16:
v = uint16(id)
case reflect.Uint32:
v = uint32(id)
case reflect.Uint64:
v = uint64(id)
case reflect.Uint:
v = uint(id)
}
return v
}
func isPKZero(pk core.PK) bool {
for _, k := range pk {
if isZero(k) {

22
helpers_test.go Normal file
View File

@ -0,0 +1,22 @@
package xorm
import "testing"
func TestSplitTag(t *testing.T) {
var cases = []struct {
tag string
tags []string
}{
{"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}},
{"TEXT", []string{"TEXT"}},
{"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}},
{"json binary", []string{"json", "binary"}},
}
for _, kase := range cases {
tags := splitTag(kase.tag)
if !sliceEq(tags, kase.tags) {
t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags)
}
}
}

View File

@ -41,7 +41,7 @@ func parseURL(connstr string) (string, error) {
return "", err
}
if u.Scheme != "postgres" {
if u.Scheme != "postgresql" && u.Scheme != "postgres" {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
}
@ -103,7 +103,7 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.POSTGRES}
o := make(values)
var err error
if strings.HasPrefix(dataSourceName, "postgres://") {
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName)
if err != nil {
return nil, err

File diff suppressed because it is too large Load Diff

View File

@ -39,45 +39,46 @@ type exprParam struct {
// statement save all the sql info for executing SQL
type Statement struct {
RefTable *core.Table
Engine *Engine
Start int
LimitN int
WhereStr string
IdParam *core.PK
Params []interface{}
OrderStr string
JoinStr string
GroupByStr string
HavingStr string
ColumnStr string
selectStr string
columnMap map[string]bool
useAllCols bool
OmitStr string
ConditionStr string
AltTableName string
RawSQL string
RawParams []interface{}
UseCascade bool
UseAutoJoin bool
StoreEngine string
Charset string
BeanArgs []interface{}
UseCache bool
UseAutoTime bool
IsDistinct bool
IsForUpdate bool
TableAlias string
allUseBool bool
checkVersion bool
unscoped bool
mustColumnMap map[string]bool
nullableMap map[string]bool
inColumns map[string]*inParam
incrColumns map[string]incrParam
decrColumns map[string]decrParam
exprColumns map[string]exprParam
RefTable *core.Table
Engine *Engine
Start int
LimitN int
WhereStr string
IdParam *core.PK
Params []interface{}
OrderStr string
JoinStr string
GroupByStr string
HavingStr string
ColumnStr string
selectStr string
columnMap map[string]bool
useAllCols bool
OmitStr string
ConditionStr string
AltTableName string
RawSQL string
RawParams []interface{}
UseCascade bool
UseAutoJoin bool
StoreEngine string
Charset string
BeanArgs []interface{}
UseCache bool
UseAutoTime bool
noAutoCondition bool
IsDistinct bool
IsForUpdate bool
TableAlias string
allUseBool bool
checkVersion bool
unscoped bool
mustColumnMap map[string]bool
nullableMap map[string]bool
inColumns map[string]*inParam
incrColumns map[string]incrParam
decrColumns map[string]decrParam
exprColumns map[string]exprParam
}
// init
@ -103,6 +104,7 @@ func (statement *Statement) Init() {
statement.BeanArgs = make([]interface{}, 0)
statement.UseCache = true
statement.UseAutoTime = true
statement.noAutoCondition = false
statement.IsDistinct = false
statement.IsForUpdate = false
statement.TableAlias = ""
@ -119,6 +121,15 @@ func (statement *Statement) Init() {
statement.exprColumns = make(map[string]exprParam)
}
// NoAutoCondition
func (statement *Statement) NoAutoCondition(no ...bool) *Statement {
statement.noAutoCondition = true
if len(no) > 0 {
statement.noAutoCondition = no[0]
}
return statement
}
// add the raw sql statement
func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement {
statement.RawSQL = querystring
@ -182,7 +193,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
return statement
}
// Auto generating conditions according a struct
// Auto generating update columnes and values according a struct
func buildUpdates(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool,
@ -211,10 +222,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
continue
}
if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text {
continue
}
fieldValuePtr, err := col.ValueOf(bean)
if err != nil {
engine.LogError(err)
@ -338,7 +345,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
// fix non-int pk issues
//if pkField.Int() != 0 {
if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface()
} else {
@ -364,11 +370,13 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
}
}
case reflect.Array, reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
if !requiredField {
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
}
if col.SQLType.IsText() {
@ -1122,12 +1130,14 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
var addedTableName = (len(statement.JoinStr) > 0)
colNames, args := buildConditions(statement.Engine, table, bean, true, true,
false, true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
if !statement.noAutoCondition {
colNames, args := buildConditions(statement.Engine, table, bean, true, true,
false, true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ")
statement.BeanArgs = args
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ")
statement.BeanArgs = args
}
var columnStr string = statement.ColumnStr
if len(statement.selectStr) > 0 {
@ -1183,12 +1193,14 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
var addedTableName = (len(statement.JoinStr) > 0)
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false,
true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
if !statement.noAutoCondition {
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false,
true, statement.allUseBool, statement.useAllCols,
statement.unscoped, statement.mustColumnMap, statement.TableName(), addedTableName)
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ")
statement.BeanArgs = args
statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ")
statement.BeanArgs = args
}
// count(index fieldname) > count(0) > count(*)
var id string = "*"

View File

@ -17,7 +17,7 @@ import (
)
const (
Version string = "0.4.4.1029"
Version string = "0.4.5.0102"
)
func regDrvsNDialects() bool {