v0.1.2 Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction

This commit is contained in:
Lunny Xiao 2013-05-12 21:37:10 +08:00
parent 8c81e70188
commit a2ebf21969
6 changed files with 204 additions and 79 deletions

View File

@ -16,6 +16,7 @@ Drivers for Go's sql package which currently support database/sql includes:
## Changelog
* **v0.1.2** : Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction
* **v0.1.1** : Add Id, In functions and improved README
* **v0.1.0** : Inital release.
@ -39,7 +40,7 @@ Drivers for Go's sql package which currently support database/sql includes:
1.Create a database engine just like sql.Open, commonly you just need create once.
```
```Go
import (
_ "github.com/Go-SQL-Driver/MySQL"
"github.com/lunny/xorm"
@ -49,7 +50,7 @@ engine := xorm.Create("mysql", "root:123@/test?charset=utf8")
or
```
```Go
import (
_ "github.com/mattn/go-sqlite3"
"github.com/lunny/xorm"
@ -59,13 +60,13 @@ engine = xorm.Create("sqlite3", "./test.db")
1.1.If you want to show all generated SQL
```
```Go
engine.ShowSQL = true
```
2.Define a struct
```
```Go
type User struct {
Id int
Name string
@ -77,7 +78,7 @@ type User struct {
3.When you set up your program, you can use CreateTables to create database tables.
```
```Go
err := engine.CreateTables(&User{})
// or err := engine.Map(&User{}, &Article{})
// err = engine.CreateAll()
@ -85,13 +86,13 @@ err := engine.CreateTables(&User{})
4.then, insert an struct to table
```
```Go
id, err := engine.Insert(&User{Name:"lunny"})
```
or if you want to update records
```
```Go
user := User{Name:"xlw"}
rows, err := engine.Update(&user, &User{Id:1})
// or rows, err := engine.Where("id = ?", 1).Update(&user)
@ -100,7 +101,7 @@ rows, err := engine.Update(&user, &User{Id:1})
5.Fetch a single object by user
```
```Go
var user = User{Id:27}
err := engine.Get(&user)
// or err := engine.Id(27).Get(&user)
@ -111,42 +112,42 @@ err := engine.Get(&user)
6.Fetch multipe objects, use Find
```
```Go
var everyone []Userinfo
err := engine.Find(&everyone)
```
6.1 also you can use Where, Limit
```
```Go
var allusers []Userinfo
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
```
6.2 or you can use a struct query
```
```Go
var tenusers []Userinfo
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0
```
6.3 or In function
```
```Go
var tenusers []Userinfo
err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5)
```
7.Delete
```
```Go
err := engine.Delete(&User{Id:1})
// or err := engine.Id(1).Delete(&User{})
```
8.Count
```
```Go
total, err := engine.Count(&User{Name:"xlw"})
```
@ -155,14 +156,14 @@ Of course, SQL execution is also provided.
1.if select then use Query
```
```Go
sql := "select * from userinfo"
results, err := engine.Query(sql)
```
2.if insert, update or delete then use Exec
```
```Go
sql = "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1)
```
@ -170,7 +171,7 @@ res, err := engine.Exec(sql, "xiaolun", 1)
##Advanced Usage
for deep usage, you should create a session, this func will create a database connection immediatelly
```
```Go
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
@ -180,7 +181,7 @@ if err != nil {
1.Fetch a single object by where
```
```Go
var user Userinfo
session.Where("id=?", 27).Get(&user)
@ -193,7 +194,7 @@ session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more compl
2.Fetch multiple objects
```
```Go
var allusers []Userinfo
err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
@ -206,7 +207,7 @@ err := session.Find(&everyone)
3.Transaction
```
```Go
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
@ -237,7 +238,7 @@ if err != nil {
4.Mixed Transaction
```
```Go
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
@ -304,7 +305,7 @@ Another is use field tag, field tag support the below keywords which split with
For Example
```
```Go
type Userinfo struct {
Uid int `xorm:"id pk not null autoincr"`
Username string
@ -321,7 +322,7 @@ Please visit [GoWalker](http://gowalker.org/github.com/lunny/xorm)
Use space.
```
```Go
type User struct {
Name string `json:"name" xorm:"name"`
}

View File

@ -14,6 +14,7 @@ xorm是一个Go语言的ORM库. 通过它可以使数据库操作非常简便。
## 更新日志
* **v0.1.2** : Insert函数支持混合struct和slice指针传入并根据数据库类型自动批量插入同时自动添加事务
* **v0.1.1** : 添加 Id, In 函数,改善 README 文档
* **v0.1.0** : 初始化工程
@ -36,7 +37,7 @@ xorm是一个Go语言的ORM库. 通过它可以使数据库操作非常简便。
1.创建数据库引擎这个函数的参数和sql.Open相同但不会立即创建连接 (例如: mysql)
```
```Go
import (
_ "github.com/Go-SQL-Driver/MySQL"
"github.com/lunny/xorm"
@ -46,7 +47,7 @@ engine := xorm.Create("mysql", "root:123@/test?charset=utf8")
or
```
```Go
import (
_ "github.com/mattn/go-sqlite3"
"github.com/lunny/xorm"
@ -56,13 +57,13 @@ engine = xorm.Create("sqlite3", "./test.db")
1.1.默认将不会显示自动生成的SQL语句如果要显示则需要设置
```
```Go
engine.ShowSQL = true
```
2.所有的ORM操作都针对一个或多个结构体一个结构体对应一张表定义一个结构体如下
```
```Go
type User struct {
Id int
Name string
@ -74,19 +75,19 @@ type User struct {
3.在程序初始化时,可能会需要创建表
```
```Go
err := engine.CreateTables(&User{})
```
4.然后,可以将一个结构体作为一条记录插入到表中。
```
```Go
id, err := engine.Insert(&User{Name:"lunny"})
```
或者执行更新操作:
```
```Go
user := User{Name:"xlw"}
rows, err := engine.Update(&user, &User{Id:1})
// rows, err := engine.Where("id = ?", 1).Update(&user)
@ -95,7 +96,7 @@ rows, err := engine.Update(&user, &User{Id:1})
5.获取单个对象可以用Get方法
```
```Go
var user = User{Id:27}
err := engine.Get(&user)
// or err := engine.Id(27).Get(&user)
@ -105,42 +106,42 @@ err := engine.Get(&user)
6.获取多个对象可以用Find方法
```
```Go
var everyone []Userinfo
err := engine.Find(&everyone)
```
6.1 你也可以使用Where和Limit方法设定条件和查询数量
```
```Go
var allusers []Userinfo
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
```
6.2 用一个结构体作为查询条件也是允许的
```
```Go
var tenusers []Userinfo
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0
```
6.3 也可以调用In函数
```
```Go
var tenusers []Userinfo
err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5)
```
7.Delete方法
```
```Go
err := engine.Delete(&User{Id:1})
// or err := engine.Id(1).Delete(&User{})
```
8.Count方法
```
```Go
total, err := engine.Count(&User{Name:"xlw"})
```
@ -149,14 +150,14 @@ total, err := engine.Count(&User{Name:"xlw"})
如果执行Select请用Query()
```
```Go
sql := "select * from userinfo"
results, err := engine.Query(sql)
```
如果执行Insert Update Delete 等操作请用Exec()
```
```Go
sql = "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1)
```
@ -165,7 +166,7 @@ res, err := engine.Exec(sql, "xiaolun", 1)
<a name="mapping" id="mapping"></a>
更高级的用法我们必须要使用session对象session对象在创建时会立刻创建一个数据库连接。
```
```Go
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
@ -175,7 +176,7 @@ if err != nil {
1.session对象同样也可以查询
```
```Go
var user Userinfo
session.Where("id=?", 27).Get(&user)
@ -188,7 +189,7 @@ session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more compl
2.获取多个对象
```
```Go
var allusers []Userinfo
err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
@ -201,7 +202,7 @@ err := session.Find(&everyone)
3.事务处理
```
```Go
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
@ -232,7 +233,7 @@ if err != nil {
4.混合型事务这个事务中既有直接的SQL语句又有ORM方法
```
```Go
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
@ -298,7 +299,7 @@ UserInfo中的成员UserName将会自动对应名为user_name的字段。
</table>
例如:
```
```Go
type Userinfo struct {
Uid int `xorm:"id pk not null autoincr"`
Username string
@ -316,7 +317,7 @@ type Userinfo struct {
答案:使用空格分开
```
```Go
type User struct {
Name string `json:"name" xorm:"name"`
}

View File

@ -23,6 +23,7 @@ type Engine struct {
Tables map[reflect.Type]Table
AutoIncrement string
ShowSQL bool
InsertMany bool
QuoteIdentifier string
Statement Statement
}

View File

@ -11,16 +11,18 @@ import (
)
type Session struct {
Db *sql.DB
Engine *Engine
Tx *sql.Tx
Statement Statement
IsAutoCommit bool
Db *sql.DB
Engine *Engine
Tx *sql.Tx
Statement Statement
IsAutoCommit bool
IsCommitedOrRollbacked bool
}
func (session *Session) Init() {
session.Statement = Statement{}
session.IsAutoCommit = true
session.IsCommitedOrRollbacked = false
}
func (session *Session) Close() {
@ -69,27 +71,39 @@ func (session *Session) Having(conditions string) *Session {
}
func (session *Session) Begin() error {
session.IsAutoCommit = false
tx, err := session.Db.Begin()
session.Tx = tx
if session.Engine.ShowSQL {
fmt.Println("BEGIN TRANSACTION")
if session.IsAutoCommit {
session.IsAutoCommit = false
session.IsCommitedOrRollbacked = false
tx, err := session.Db.Begin()
session.Tx = tx
if session.Engine.ShowSQL {
fmt.Println("BEGIN TRANSACTION")
}
return err
}
return err
return nil
}
func (session *Session) Rollback() error {
if session.Engine.ShowSQL {
fmt.Println("ROLL BACK")
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
if session.Engine.ShowSQL {
fmt.Println("ROLL BACK")
}
session.IsCommitedOrRollbacked = true
return session.Tx.Rollback()
}
return session.Tx.Rollback()
return nil
}
func (session *Session) Commit() error {
if session.Engine.ShowSQL {
fmt.Println("COMMIT")
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
if session.Engine.ShowSQL {
fmt.Println("COMMIT")
}
session.IsCommitedOrRollbacked = true
return session.Tx.Commit()
}
return session.Tx.Commit()
return nil
}
func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
@ -359,13 +373,108 @@ func (session *Session) Query(sql string, paramStr ...interface{}) (resultsSlice
func (session *Session) Insert(beans ...interface{}) (int64, error) {
var lastId int64 = -1
var err error = nil
isInTransaction := !session.IsAutoCommit
if !isInTransaction {
session.Begin()
}
for _, bean := range beans {
lastId, err := session.InsertOne(bean)
if err != nil {
return lastId, err
sliceValue := reflect.Indirect(reflect.ValueOf(bean))
if sliceValue.Kind() == reflect.Slice {
if session.Engine.InsertMany {
lastId, err = session.InsertMulti(bean)
if err != nil {
if !isInTransaction {
session.Rollback()
}
return lastId, err
}
} else {
size := sliceValue.Len()
for i := 0; i < size; i++ {
lastId, err = session.InsertOne(sliceValue.Index(i).Interface())
if err != nil {
if !isInTransaction {
session.Rollback()
}
return lastId, err
}
}
}
} else {
lastId, err = session.InsertOne(bean)
if err != nil {
if !isInTransaction {
session.Rollback()
}
return lastId, err
}
}
}
return lastId, nil
if !isInTransaction {
err = session.Commit()
}
return lastId, err
}
func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
if sliceValue.Kind() != reflect.Slice {
return -1, errors.New("needs a pointer to a slice")
}
bean := sliceValue.Index(0).Interface()
sliceElementType := Type(bean)
table := session.Engine.Tables[sliceElementType]
session.Statement.Table = &table
size := sliceValue.Len()
colNames := make([]string, 0)
colMultiPlaces := make([]string, 0)
var args = make([]interface{}, 0)
for i := 0; i < size; i++ {
elemValue := sliceValue.Index(i).Interface()
colPlaces := make([]string, 0)
for _, col := range table.Columns {
fieldValue := reflect.Indirect(reflect.ValueOf(elemValue)).FieldByName(col.FieldName)
val := fieldValue.Interface()
if col.IsAutoIncrement && fieldValue.Int() == 0 {
continue
}
args = append(args, val)
if i == 0 {
colNames = append(colNames, col.Name)
}
colPlaces = append(colPlaces, "?")
}
colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", "))
}
statement := fmt.Sprintf("INSERT INTO %v%v%v (%v) VALUES (%v)",
session.Engine.QuoteIdentifier,
table.Name,
session.Engine.QuoteIdentifier,
strings.Join(colNames, ", "),
strings.Join(colMultiPlaces, "),("))
res, err := session.Exec(statement, args...)
if err != nil {
return -1, err
}
id, err := res.LastInsertId()
if err != nil {
return -1, err
}
return id, nil
}
func (session *Session) InsertOne(bean interface{}) (int64, error) {

View File

@ -10,6 +10,7 @@ func Create(driverName string, dataSourceName string) Engine {
engine.Tables = make(map[reflect.Type]Table)
engine.Statement.Engine = &engine
engine.InsertMany = true
if driverName == SQLITE {
engine.AutoIncrement = "AUTOINCREMENT"
} else {

View File

@ -106,24 +106,33 @@ func insertAutoIncr(t *testing.T) {
}
func insertMulti(t *testing.T) {
user1 := Userinfo{Username: "xlw", Departname: "dev", Alias: "lunny2", Created: time.Now()}
user2 := Userinfo{Username: "xlw2", Departname: "dev", Alias: "lunny3", Created: time.Now()}
_, err := engine.Insert(&user1, &user2)
users := []*Userinfo{
{Username: "xlw", Departname: "dev", Alias: "lunny2", Created: time.Now()},
{Username: "xlw2", Departname: "dev", Alias: "lunny3", Created: time.Now()},
}
_, err := engine.Insert(&users)
if err != nil {
t.Error(err)
}
engine.InsertMany = false
users = []*Userinfo{
{Username: "xlw9", Departname: "dev", Alias: "lunny9", Created: time.Now()},
{Username: "xlw10", Departname: "dev", Alias: "lunny10", Created: time.Now()},
}
_, err = engine.Insert(&users)
if err != nil {
t.Error(err)
}
engine.InsertMany = true
}
func insertTwoTable(t *testing.T) {
userinfo := Userinfo{Username: "xlw3", Departname: "dev", Alias: "lunny4", Created: time.Now()}
uid, err := engine.Insert(&userinfo)
if err != nil {
t.Error(err)
return
}
userdetail := Userdetail{Uid: int(uid), Intro: "I'm a very beautiful women.", Profile: "sfsaf"}
_, err = engine.Insert(&userdetail)
userdetail := Userdetail{Uid: 1, Intro: "I'm a very beautiful women.", Profile: "sfsaf"}
_, err := engine.Insert(&userinfo, &userdetail)
if err != nil {
t.Error(err)
}
@ -199,7 +208,8 @@ func in(t *testing.T) {
}
fmt.Println(users)
err = engine.Where("id > ?", 2).In("id", 1, 2, 3).Find(&users)
ids := []interface{}{1, 2, 3}
err = engine.Where("id > ?", 2).In("id", ids...).Find(&users)
if err != nil {
t.Error(err)
return
@ -352,6 +362,7 @@ func TestMysql(t *testing.T) {
exec(t)
insertAutoIncr(t)
insertMulti(t)
insertTwoTable(t)
update(t)
delete(t)
get(t)
@ -378,6 +389,7 @@ func TestSqlite(t *testing.T) {
exec(t)
insertAutoIncr(t)
insertMulti(t)
insertTwoTable(t)
update(t)
delete(t)
get(t)