add sql execution support

This commit is contained in:
Lunny Xiao 2013-05-08 21:42:22 +08:00
parent 74e0e3b175
commit 5870dbaab0
8 changed files with 574 additions and 320 deletions

147
README.md
View File

@ -7,12 +7,10 @@ xorm is an ORM for Go. It lets you map Go structs to tables in a database.
Right now, it interfaces with Mysql/SQLite. The goal however is to add support for PostgreSQL/DB2/MS ADODB/ODBC/Oracle in the future. Right now, it interfaces with Mysql/SQLite. The goal however is to add support for PostgreSQL/DB2/MS ADODB/ODBC/Oracle in the future.
All in all, it's not entirely ready for advanced use yet, but it's getting there. All in all, it's not entirely ready for product use yet, but it's getting there.
Drivers for Go's sql package which support database/sql includes: Drivers for Go's sql package which support database/sql includes:
Mysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
@ -55,19 +53,45 @@ then, insert an struct to table
or you want to update this struct or you want to update this struct
user := User{Id:1, Name:"xlw"} user := User{Name:"xlw"}
rows, err := engine.Update(&user) rows, err := engine.Update(&user, &User{Id:1})
// rows, err := engine.Where("id = ?", 1).Update(&user)
3.Fetch a single object by user 3.Fetch a single object by user
var user = User{Id:27} var user = User{Id:27}
engine.Get(&user) err := engine.Get(&user)
var user = User{Name:"xlw"} var user = User{Name:"xlw"}
engine.Get(&user) err := engine.Get(&user)
4.Fetch multipe objects, use Find
var allusers []Userinfo
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
var tenusers []Userinfo
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 if omit offset the default is 0
var everyone []Userinfo
err := engine.Find(&everyone)
5.Delete and Count
err := engine.Delete(&User{Id:1})
total, err := engine.Count(&User{Name:"xlw"})
##Origin Use
Of course, the basic usage is also provided.
sql := "select * from userinfo"
results, err := engine.Query(sql)
sql = "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1)
##Deep Use ##Deep Use
for deep use, you should create a session, this func will create a connection to db for deep use, you should create a session, this func will create a connection to db
@ -82,49 +106,120 @@ for deep use, you should create a session, this func will create a connection to
1.Fetch a single object by where 1.Fetch a single object by where
var user Userinfo var user Userinfo
session.Where("id=?", 27).Get(&user) session.Where("id=?", 27).Get(&user)
var user2 Userinfo var user2 Userinfo
session.Where(3).Get(&user2) // this is shorthand for the version above
var user3 Userinfo
session.Where("name = ?", "john").Get(&user3) // more complex query session.Where("name = ?", "john").Get(&user3) // more complex query
var user4 Userinfo var user3 Userinfo
session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more complex session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more complex
2.Fetch multiple objects 2.Fetch multiple objects
var allusers []Userinfo var allusers []Userinfo
err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20 err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
var tenusers []Userinfo var tenusers []Userinfo
err := session.Where("id > ?", "3").Limit(10).Find(&tenusers) //Get id>3 limit 10 if omit offset the default is 0 err := session.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 if omit offset the default is 0
var everyone []Userinfo var everyone []Userinfo
err := session.Find(&everyone) err := session.Find(&everyone)
3.Transaction
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
session.Rollback()
return
}
_, err = session.Delete(&user2)
if err != nil {
session.Rollback()
return
}
// add Commit() after all actions
err = session.Commit()
if err != nil {
return
}
4.Mixed Transaction
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
session.Rollback()
return
}
_, err = session.Exec("delete from userinfo where username = ?", user2.Username)
if err != nil {
session.Rollback()
return
}
// add Commit() after all actions
err = session.Commit()
if err != nil {
return
}
##Mapping Rules
##***Mapping Rules***
1.Struct and struct's fields name should be Pascal style, and the table and column's name default is us 1.Struct and struct's fields name should be Pascal style, and the table and column's name default is us
for example:
For example:
The structs Name 'UserInfo' will turn into the table name 'user_info', the same as the keyname. The structs Name 'UserInfo' will turn into the table name 'user_info', the same as the keyname.
If the keyname is 'UserName' will turn into the select colum 'user_name' If the keyname is 'UserName' will turn into the select colum 'user_name'
2.You have two method to change the rule. One is implement your own Map interface according IMapper, you can find the interface in mapper.go and set it to engine.Mapper 2.You have two method to change the rule. One is implement your own Map interface according IMapper, you can find the interface in mapper.go and set it to engine.Mapper
another is use field tag, field tag support the below keywords: another is use field tag, field tag support the below keywords:
* [name] column name
* pk the field is a primary key <table>
* int(11)/varchar(50) column type <tr>
* autoincr auto incrment <td>name</td><td>column name</td>
* [not ]null if column can be null value </tr>
* unique unique <tr>
* \- this field is not map as a table column <td>pk</td><td>the field is a primary key</td>
</tr>
<tr>
<td>int(11)/varchar(50)</td><td>column type</td>
</tr>
<tr>
<td>autoincr</td><td>auto incrment</td>
</tr>
<tr>
<td>[not ]null</td><td>if column can be null value</td>
</tr>
<tr>
<td>unique</td><td>unique</td>
</tr>
<tr>
<td>-</td><td>this field is not map as a table column</td>
</tr>
</table>
##FAQ ##FAQ
1.How the xorm tag use both with json? 1.How the xorm tag use both with json?

View File

@ -1,127 +1,234 @@
xorm # xorm
===== ===========
[English](README.md) [English](README.md)
xorm 是一个Go语言的ORM对象关系模型. It lets you map Go structs to tables in a database. xorm是一个Go语言的ORM库. 通过它可以简化对数据库的操作。
目前仅支持Mysql和SQLite当然我们的目标是支持PostgreSQL/DB2/MS ADODB/ODBC/Oracle等等。
Right now, it interfaces with Mysql/SQLite. The goal however is to add support for PostgreSQL/DB2/MS ADODB/ODBC/Oracle in the future. 但是,目前的版本还不可用于正式版本。
All in all, it's not entirely ready for advanced use yet, but it's getting there. 目前支持的Go数据库驱动如下
Drivers for Go's sql package which support database/sql includes: Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
Mysql:[github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
Mysql:[github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) ## 安装
go get github.com/lunny/xorm
SQLite:[github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) ## 快速开始
### Installing xorm 1.创建数据库引擎 (例如: mysql)
go get github.com/lunny/xorm
### Quick Start engine := xorm.Create("mysql://root:123@localhost/test")
1. Create an database engine (for example: mysql)
```go 2.定义你的Struct
engine := xorm.Create("mysql://root:123@localhost/test")
```
2. Define your struct
```go type User struct {
type User struct { Id int
Id int Name string
Name string Age int `xorm:"-"`
Age int `xorm:"-"` }
}
```
for Simple Task, just use engine's functions:
begin start, you should create a database and then we create the tables 对于简单的任务可以只用engine一个对象就可以完成操作。
首先需要创建一个数据库然后使用以下语句创建一个Struct对应的表。
err := engine.CreateTables(&User{})
```go
err := engine.CreateTables(&User{})
```
then, insert an struct to table 然后,可以将一个结构体作为一条记录插入到表中。
```go
id, err := engine.Insert(&User{Name:"lunny"})
```
or you want to update this struct id, err := engine.Insert(&User{Name:"lunny"})
```go
user := User{Id:1, Name:"xlw"}
rows, err := engine.Update(&user)
```
3. Fetch a single object by user 或者执行更新操作:
```go
var user = User{Id:27}
engine.Get(&user)
var user = User{Name:"xlw"}
engine.Get(&user)
```
for deep use, you should create a session, this func will create a connection to db user := User{Name:"xlw"}
rows, err := engine.Update(&user, &User{Id:1})
// rows, err := engine.Where("id = ?", 1).Update(&user)
```go
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
return
}
```
1. Fetch a single object by where 3.获取单个对象可以用Get方法
```go
var user Userinfo
session.Where("id=?", 27).Get(&user)
var user2 Userinfo
session.Where(3).Get(&user2) // this is shorthand for the version above
var user3 Userinfo var user = User{Id:27}
session.Where("name = ?", "john").Get(&user3) // more complex query err := engine.Get(&user)
var user4 Userinfo var user = User{Name:"xlw"}
session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more complex err := engine.Get(&user)
```
4.获取多个对象可以用Find方法
var allusers []Userinfo
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
2. Fetch multiple objects var tenusers []Userinfo
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 if omit offset the default is 0
```go var everyone []Userinfo
var allusers []Userinfo err := engine.Find(&everyone)
err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
5.另外还有Delete和Count方法
err := engine.Delete(&User{Id:1})
total, err := engine.Count(&User{Name:"xlw"})
##Origin Use
当然如果你想直接使用SQL语句进行操作也是允许的。
sql := "select * from userinfo"
results, err := engine.Query(sql)
sql = "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1)
var tenusers []Userinfo ##Deep Use
err := session.Where("id > ?", "3").Limit(10).Find(&tenusers) //Get id>3 limit 10 if omit offset the default is 0 更高级的用法我们必须要使用session对象session对象在创建时会创建一个数据库连接。
var everyone []Userinfo
err := session.Find(&everyone)
```
###***About Map Rules*** session, err := engine.MakeSession()
1. Struct and struct's fields name should be Pascal style, and the table and column's name default is us defer session.Close()
for example: if err != nil {
The structs Name 'UserInfo' will turn into the table name 'user_info', the same as the keyname. return
If the keyname is 'UserName' will turn into the select colum 'user_name' }
2. You have two method to change the rule. One is implement your own Map interface according IMapper, you can find the interface in mapper.go and set it to engine.Mapper
another is use field tag, field tag support the below keywords: 1.session对象同样也可以查询
[name] column name
pk the field is a primary key var user Userinfo
int(11)/varchar(50) column type session.Where("id=?", 27).Get(&user)
autoincr auto incrment
[not ]null if column can be null value var user2 Userinfo
unique unique session.Where("name = ?", "john").Get(&user3) // more complex query
- this field is not map as a table column
var user3 Userinfo
session.Where("name = ? and age < ?", "john", 88).Get(&user4) // even more complex
2.获取多个对象
var allusers []Userinfo
err := session.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
var tenusers []Userinfo
err := session.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 if omit offset the default is 0
var everyone []Userinfo
err := session.Find(&everyone)
3.事务处理
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
session.Rollback()
return
}
_, err = session.Delete(&user2)
if err != nil {
session.Rollback()
return
}
// add Commit() after all actions
err = session.Commit()
if err != nil {
return
}
4.混合型事务这个事务中既有直接的SQL语句又有其它方法
// add Begin() before any action
session.Begin()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
return
}
user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2)
if err != nil {
session.Rollback()
return
}
_, err = session.Exec("delete from userinfo where username = ?", user2.Username)
if err != nil {
session.Rollback()
return
}
// add Commit() after all actions
err = session.Commit()
if err != nil {
return
}
##Mapping Rules
1.Struct 和 Struct 的field名字应该为Pascal式命名默认的映射规则将转换成用下划线连接的命名规则这个映射是自动进行的当然你可以通过修改Engine或者Session的成员IMapper来改变它。
例如:
结构体的名字UserInfo将会自动对应数据库中的名为user_info的表。
UserInfo中的成员UserName将会自动对应名为user_name的字段。
2.当然你也可以改变这个规则这有两种方法。一是实现你自己的IMapper你可以在mapper.go中查看到这个借口。然后设置到 engine.Mapper这将影响所有的Session或者你可以设置到某一个session那么只会影响到这个session对应的操作。
另外一种方法就通过Field Tag来进行改变关于Field Tag请参考Go的语言文档如下列出了Tag中可用的关键字及其对应的意义
<table>
<tr>
<td>name</td><td>当前field对应的字段的名称可选</td>
</tr>
<tr>
<td>pk</td><td>是否是Primary Key</td>
</tr>
<tr>
<td>int(11)/varchar(50)</td><td>字段类型</td>
</tr>
<tr>
<td>autoincr</td><td>是否是自增</td>
</tr>
<tr>
<td>[not ]null</td><td>是否可以为空</td>
</tr>
<tr>
<td>unique</td><td>是否是唯一</td>
</tr>
<tr>
<td>-</td><td>这个Field将不进行字段映射</td>
</tr>
</table>
##FAQ
1.xorm的tag和json的tag如何同时起作用
使用空格分开
type User struct {
User string `json:"user" orm:"user_id"`
}
## LICENSE ## LICENSE

View File

@ -341,6 +341,24 @@ func (e *Engine) CreateAll() error {
return err return err
} }
func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) {
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
return nil, err
}
return session.Exec(sql, args...)
}
func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
return nil, err
}
return session.Query(sql, paramStr...)
}
func (engine *Engine) Insert(beans ...interface{}) (int64, error) { func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
session, err := engine.MakeSession() session, err := engine.MakeSession()
defer session.Close() defer session.Close()
@ -348,21 +366,19 @@ func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
return -1, err return -1, err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement)
return session.Insert(beans...) return session.Insert(beans...)
} }
func (engine *Engine) Update(bean interface{}) (int64, error) { func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) {
session, err := engine.MakeSession() session, err := engine.MakeSession()
defer session.Close() defer session.Close()
if err != nil { if err != nil {
return -1, err return -1, err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement) return session.Update(bean, condiBeans...)
return session.Update(bean)
} }
func (engine *Engine) Delete(bean interface{}) (int64, error) { func (engine *Engine) Delete(bean interface{}) (int64, error) {
@ -372,8 +388,7 @@ func (engine *Engine) Delete(bean interface{}) (int64, error) {
return -1, err return -1, err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement)
return session.Delete(bean) return session.Delete(bean)
} }
@ -384,21 +399,19 @@ func (engine *Engine) Get(bean interface{}) error {
return err return err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement)
return session.Get(bean) return session.Get(bean)
} }
func (engine *Engine) Find(beans interface{}) error { func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error {
session, err := engine.MakeSession() session, err := engine.MakeSession()
defer session.Close() defer session.Close()
if err != nil { if err != nil {
return err return err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement) return session.Find(beans, condiBeans...)
return session.Find(beans)
} }
func (engine *Engine) Count(bean interface{}) (int64, error) { func (engine *Engine) Count(bean interface{}) (int64, error) {
@ -408,7 +421,6 @@ func (engine *Engine) Count(bean interface{}) (int64, error) {
return 0, err return 0, err
} }
defer engine.Statement.Init() defer engine.Statement.Init()
engine.Statement.Session = &session session.Statement = engine.Statement
session.SetStatement(&engine.Statement)
return session.Count(bean) return session.Count(bean)
} }

View File

@ -35,97 +35,75 @@ func Type2StructName(v reflect.Type) string {
} }
type Session struct { type Session struct {
Db *sql.DB Db *sql.DB
Engine *Engine Engine *Engine
Tx *sql.Tx Tx *sql.Tx
Statements []Statement Statement Statement
Mapper IMapper Mapper IMapper
IsAutoCommit bool IsAutoCommit bool
IsAutoRollback bool
CurStatementIdx int
} }
func (session *Session) Init() { func (session *Session) Init() {
session.Statements = make([]Statement, 0) session.Statement = Statement{}
session.CurStatementIdx = -1
session.IsAutoCommit = true session.IsAutoCommit = true
session.IsAutoRollback = false
} }
func (session *Session) Close() { func (session *Session) Close() {
rollbackfunc := func() {
if session.IsAutoRollback {
session.Rollback()
}
}
defer rollbackfunc()
defer session.Db.Close() defer session.Db.Close()
} }
func (session *Session) CurrentStatement() *Statement {
if session.CurStatementIdx > -1 {
return &session.Statements[session.CurStatementIdx]
}
return nil
}
func (session *Session) AutoStatement() *Statement {
if session.CurStatementIdx == -1 {
session.newStatement()
}
return session.CurrentStatement()
}
func (session *Session) Where(querystring string, args ...interface{}) *Session { func (session *Session) Where(querystring string, args ...interface{}) *Session {
statement := session.AutoStatement() session.Statement.Where(querystring, args...)
statement.Where(querystring, args...)
return session return session
} }
func (session *Session) Limit(limit int, start ...int) *Session { func (session *Session) Limit(limit int, start ...int) *Session {
statement := session.AutoStatement() session.Statement.Limit(limit, start...)
statement.Limit(limit, start...)
return session return session
} }
func (session *Session) OrderBy(order string) *Session { func (session *Session) OrderBy(order string) *Session {
statement := session.AutoStatement() session.Statement.OrderBy(order)
statement.OrderBy(order)
return session return session
} }
//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN //The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
func (session *Session) Join(join_operator, tablename, condition string) *Session { func (session *Session) Join(join_operator, tablename, condition string) *Session {
statement := session.AutoStatement() session.Statement.Join(join_operator, tablename, condition)
statement.Join(join_operator, tablename, condition)
return session return session
} }
func (session *Session) GroupBy(keys string) *Session { func (session *Session) GroupBy(keys string) *Session {
statement := session.AutoStatement() session.Statement.GroupBy(keys)
statement.GroupBy(keys)
return session return session
} }
func (session *Session) Having(conditions string) *Session { func (session *Session) Having(conditions string) *Session {
statement := session.AutoStatement() session.Statement.Having(conditions)
statement.Having(conditions)
return session return session
} }
func (session *Session) Begin() error { func (session *Session) Begin() error {
session.IsAutoCommit = false session.IsAutoCommit = false
session.IsAutoRollback = true
tx, err := session.Db.Begin() tx, err := session.Db.Begin()
session.Tx = tx session.Tx = tx
if session.Engine.ShowSQL {
fmt.Println("BEGIN TRANSACTION")
}
return err return err
} }
func (session *Session) Rollback() error { func (session *Session) Rollback() error {
if session.Engine.ShowSQL {
fmt.Println("ROLL BACK")
}
return session.Tx.Rollback() return session.Tx.Rollback()
} }
func (session *Session) Commit() error { func (session *Session) Commit() error {
if session.Engine.ShowSQL {
fmt.Println("COMMIT")
}
return session.Tx.Commit() return session.Tx.Commit()
} }
@ -133,27 +111,10 @@ func (session *Session) TableName(bean interface{}) string {
return session.Mapper.Obj2Table(StructName(bean)) return session.Mapper.Obj2Table(StructName(bean))
} }
func (session *Session) SetStatement(statement *Statement) { func (session *Session) Bean2Table(bean interface{}) *Table {
if session.CurStatementIdx == len(session.Statements)-1 { tablName := session.TableName(bean)
session.Statements = append(session.Statements, *statement) table := session.Engine.Tables[tablName]
} else { return &table
session.Statements[session.CurStatementIdx+1] = *statement
}
session.CurStatementIdx = session.CurStatementIdx + 1
}
func (session *Session) newStatement() {
if session.CurStatementIdx == len(session.Statements)-1 {
state := Statement{Session: session}
state.Init()
session.Statements = append(session.Statements, state)
}
session.CurStatementIdx = session.CurStatementIdx + 1
}
func (session *Session) clearStatment() {
session.Statements[session.CurStatementIdx].Init()
session.CurStatementIdx = session.CurStatementIdx - 1
} }
func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error { func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
@ -162,8 +123,7 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b
return errors.New("expected a pointer to a struct") return errors.New("expected a pointer to a struct")
} }
tablName := session.TableName(obj) table := session.Bean2Table(obj)
table := session.Engine.Tables[tablName]
for key, data := range objMap { for key, data := range objMap {
structField := dataStruct.FieldByName(table.Columns[key].FieldName) structField := dataStruct.FieldByName(table.Columns[key].FieldName)
@ -231,8 +191,8 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b
} }
//Execute sql //Execute sql
func (session *Session) Exec(finalQueryString string, args ...interface{}) (sql.Result, error) { func (session *Session) innerExec(sql string, args ...interface{}) (sql.Result, error) {
rs, err := session.Db.Prepare(finalQueryString) rs, err := session.Db.Prepare(sql)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -245,18 +205,30 @@ func (session *Session) Exec(finalQueryString string, args ...interface{}) (sql.
return res, nil return res, nil
} }
func (session *Session) Get(bean interface{}) error { func (session *Session) Exec(sql string, args ...interface{}) (sql.Result, error) {
statement := session.AutoStatement() if session.Engine.ShowSQL {
statement.Limit(1) fmt.Println(sql)
tableName := session.TableName(bean) }
table := session.Engine.Tables[tableName] if session.IsAutoCommit {
statement.Table = &table return session.innerExec(sql, args...)
}
return session.Tx.Exec(sql, args...)
}
colNames, args := session.BuildConditions(&table, bean) func (session *Session) Get(bean interface{}) error {
statement := session.Statement
defer session.Statement.Init()
statement.Limit(1)
table := session.Bean2Table(bean)
statement.Table = table
colNames, args := session.BuildConditions(table, bean)
statement.ColumnStr = strings.Join(colNames, " and ") statement.ColumnStr = strings.Join(colNames, " and ")
statement.BeanArgs = args statement.BeanArgs = args
resultsSlice, err := session.FindMap(statement) sql := statement.generateSql()
resultsSlice, err := session.Query(sql, append(statement.Params, statement.BeanArgs...)...)
if err != nil { if err != nil {
return err return err
} }
@ -275,16 +247,16 @@ func (session *Session) Get(bean interface{}) error {
} }
func (session *Session) Count(bean interface{}) (int64, error) { func (session *Session) Count(bean interface{}) (int64, error) {
statement := session.AutoStatement() statement := session.Statement
tableName := session.TableName(bean) defer session.Statement.Init()
table := session.Engine.Tables[tableName] table := session.Bean2Table(bean)
statement.Table = &table statement.Table = table
colNames, args := session.BuildConditions(&table, bean) colNames, args := session.BuildConditions(table, bean)
statement.ColumnStr = strings.Join(colNames, " and ") statement.ColumnStr = strings.Join(colNames, " and ")
statement.BeanArgs = args statement.BeanArgs = args
resultsSlice, err := session.SQL2Map(statement.genCountSql(), append(statement.Params, statement.BeanArgs...)) resultsSlice, err := session.Query(statement.genCountSql(), append(statement.Params, statement.BeanArgs...)...)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -298,9 +270,9 @@ func (session *Session) Count(bean interface{}) (int64, error) {
return int64(total), err return int64(total), err
} }
func (session *Session) Find(rowsSlicePtr interface{}) error { func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
statement := session.AutoStatement() statement := session.Statement
defer session.Statement.Init()
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
if sliceValue.Kind() != reflect.Slice { if sliceValue.Kind() != reflect.Slice {
return errors.New("needs a pointer to a slice") return errors.New("needs a pointer to a slice")
@ -312,7 +284,15 @@ func (session *Session) Find(rowsSlicePtr interface{}) error {
table := session.Engine.Tables[tableName] table := session.Engine.Tables[tableName]
statement.Table = &table statement.Table = &table
resultsSlice, err := session.FindMap(statement) if len(condiBean) > 0 {
colNames, args := session.BuildConditions(&table, condiBean[0])
statement.ColumnStr = strings.Join(colNames, " and ")
statement.BeanArgs = args
}
sql := statement.generateSql()
resultsSlice, err := session.Query(sql, append(statement.Params, statement.BeanArgs...)...)
if err != nil { if err != nil {
return err return err
} }
@ -328,7 +308,7 @@ func (session *Session) Find(rowsSlicePtr interface{}) error {
return nil return nil
} }
func (session *Session) SQL2Map(sqls string, paramStr []interface{}) (resultsSlice []map[string][]byte, err error) { func (session *Session) Query(sqls string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
if session.Engine.ShowSQL { if session.Engine.ShowSQL {
fmt.Println(sqls) fmt.Println(sqls)
} }
@ -397,11 +377,6 @@ func (session *Session) SQL2Map(sqls string, paramStr []interface{}) (resultsSli
return resultsSlice, nil return resultsSlice, nil
} }
func (session *Session) FindMap(statement *Statement) (resultsSlice []map[string][]byte, err error) {
sqls := statement.generateSql()
return session.SQL2Map(sqls, append(statement.Params, statement.BeanArgs...))
}
func (session *Session) Insert(beans ...interface{}) (int64, error) { func (session *Session) Insert(beans ...interface{}) (int64, error) {
var lastId int64 = -1 var lastId int64 = -1
for _, bean := range beans { for _, bean := range beans {
@ -414,8 +389,7 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) {
} }
func (session *Session) InsertOne(bean interface{}) (int64, error) { func (session *Session) InsertOne(bean interface{}) (int64, error) {
tableName := session.TableName(bean) table := session.Bean2Table(bean)
table := session.Engine.Tables[tableName]
colNames := make([]string, 0) colNames := make([]string, 0)
colPlaces := make([]string, 0) colPlaces := make([]string, 0)
@ -423,10 +397,8 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) {
for _, col := range table.Columns { for _, col := range table.Columns {
fieldValue := reflect.Indirect(reflect.ValueOf(bean)).FieldByName(col.FieldName) fieldValue := reflect.Indirect(reflect.ValueOf(bean)).FieldByName(col.FieldName)
val := fieldValue.Interface() val := fieldValue.Interface()
if col.IsAutoIncrement { if col.IsAutoIncrement && fieldValue.Int() == 0 {
if fieldValue.Int() == 0 { continue
continue
}
} }
args = append(args, val) args = append(args, val)
colNames = append(colNames, col.Name) colNames = append(colNames, col.Name)
@ -435,23 +407,12 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) {
statement := fmt.Sprintf("INSERT INTO %v%v%v (%v) VALUES (%v)", statement := fmt.Sprintf("INSERT INTO %v%v%v (%v) VALUES (%v)",
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
tableName, table.Name,
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
strings.Join(colNames, ", "), strings.Join(colNames, ", "),
strings.Join(colPlaces, ", ")) strings.Join(colPlaces, ", "))
if session.Engine.ShowSQL { res, err := session.Exec(statement, args...)
fmt.Println(statement)
}
var res sql.Result
var err error
if session.IsAutoCommit {
res, err = session.Exec(statement, args...)
} else {
res, err = session.Tx.Exec(statement, args...)
}
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -461,6 +422,7 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) {
if err != nil { if err != nil {
return -1, err return -1, err
} }
return id, nil return id, nil
} }
@ -497,46 +459,42 @@ func (session *Session) BuildConditions(table *Table, bean interface{}) ([]strin
return colNames, args return colNames, args
} }
func (session *Session) Update(bean interface{}) (int64, error) { func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
tableName := session.TableName(bean) table := session.Bean2Table(bean)
table := session.Engine.Tables[tableName] colNames, args := session.BuildConditions(table, bean)
colNames, args := session.BuildConditions(&table, bean) var condiColNames []string
var condiArgs []interface{}
if len(condiBean) > 0 {
condiColNames, condiArgs = session.BuildConditions(table, condiBean[0])
}
var condition = "" var condition = ""
st := session.AutoStatement() st := session.Statement
defer session.clearStatment() defer session.Statement.Init()
if st.WhereStr != "" { if st.WhereStr != "" {
condition = fmt.Sprintf("WHERE %v", st.WhereStr) condition = fmt.Sprintf("WHERE %v", st.WhereStr)
} }
if condition == "" { if condition == "" {
fieldValue := reflect.Indirect(reflect.ValueOf(bean)).FieldByName(table.PKColumn().FieldName) if len(condiColNames) > 0 {
if fieldValue.Int() != 0 { condition = fmt.Sprintf("WHERE %v ", strings.Join(condiColNames, " and "))
condition = fmt.Sprintf("WHERE %v = ?", table.PKColumn().Name) }
args = append(args, fieldValue.Interface()) } else {
if len(condiColNames) > 0 {
condition = fmt.Sprintf("%v and %v", condition, strings.Join(condiColNames, " and "))
} }
} }
statement := fmt.Sprintf("UPDATE %v%v%v SET %v %v", statement := fmt.Sprintf("UPDATE %v%v%v SET %v %v",
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
tableName, table.Name,
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
strings.Join(colNames, ", "), strings.Join(colNames, ", "),
condition) condition)
if session.Engine.ShowSQL { eargs := append(append(args, st.Params...), condiArgs...)
fmt.Println(statement) res, err := session.Exec(statement, eargs...)
}
var res sql.Result
var err error
if session.IsAutoCommit {
fmt.Println("session.Exec")
res, err = session.Exec(statement, append(args, st.Params...)...)
} else {
fmt.Println("tx.Exec")
res, err = session.Tx.Exec(statement, append(args, st.Params...)...)
}
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -550,13 +508,12 @@ func (session *Session) Update(bean interface{}) (int64, error) {
} }
func (session *Session) Delete(bean interface{}) (int64, error) { func (session *Session) Delete(bean interface{}) (int64, error) {
tableName := session.TableName(bean) table := session.Bean2Table(bean)
table := session.Engine.Tables[tableName] colNames, args := session.BuildConditions(table, bean)
colNames, args := session.BuildConditions(&table, bean)
var condition = "" var condition = ""
st := session.AutoStatement() st := session.Statement
defer session.clearStatment() defer session.Statement.Init()
if st.WhereStr != "" { if st.WhereStr != "" {
condition = fmt.Sprintf("WHERE %v", st.WhereStr) condition = fmt.Sprintf("WHERE %v", st.WhereStr)
if len(colNames) > 0 { if len(colNames) > 0 {
@ -569,21 +526,12 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
statement := fmt.Sprintf("DELETE FROM %v%v%v %v", statement := fmt.Sprintf("DELETE FROM %v%v%v %v",
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
tableName, table.Name,
session.Engine.QuoteIdentifier, session.Engine.QuoteIdentifier,
condition) condition)
if session.Engine.ShowSQL { res, err := session.Exec(statement, append(st.Params, args...)...)
fmt.Println(statement)
}
var res sql.Result
var err error
if session.IsAutoCommit {
res, err = session.Exec(statement, append(st.Params, args...)...)
} else {
res, err = session.Tx.Exec(statement, append(st.Params, args...)...)
}
if err != nil { if err != nil {
return -1, err return -1, err
} }

View File

@ -6,7 +6,7 @@ import (
type Statement struct { type Statement struct {
Table *Table Table *Table
Session *Session Engine *Engine
Start int Start int
LimitN int LimitN int
WhereStr string WhereStr string
@ -21,7 +21,6 @@ type Statement struct {
func (statement *Statement) Init() { func (statement *Statement) Init() {
statement.Table = nil statement.Table = nil
statement.Session = nil
statement.Start = 0 statement.Start = 0
statement.LimitN = 0 statement.LimitN = 0
statement.WhereStr = "" statement.WhereStr = ""
@ -76,13 +75,8 @@ func (statement Statement) genCountSql() string {
return statement.genSelectSql("count(*) as total") return statement.genSelectSql("count(*) as total")
} }
func (statement Statement) genExecSql() string {
return ""
}
func (statement Statement) genSelectSql(columnStr string) (a string) { func (statement Statement) genSelectSql(columnStr string) (a string) {
session := statement.Session if statement.Engine.Protocol == "mssql" {
if session.Engine.Protocol == "mssql" {
if statement.Start > 0 { if statement.Start > 0 {
a = fmt.Sprintf("select ROW_NUMBER() OVER(order by %v )as rownum,%v from %v", a = fmt.Sprintf("select ROW_NUMBER() OVER(order by %v )as rownum,%v from %v",
statement.Table.PKColumn().Name, statement.Table.PKColumn().Name,
@ -171,24 +165,3 @@ func (statement Statement) genSelectSql(columnStr string) (a string) {
} }
return return
} }
/*func (statement *Statement) genInsertSQL() string {
table = statement.Table
colNames := make([]string, len(table.Columns))
for idx, col := range table.Columns {
if col.Name == "" {
continue
}
colNames[idx] = col.Name
}
return strings.Join(colNames, ", ")
colNames := make([]string, len(table.Columns))
for idx, col := range table.Columns {
if col.Name == "" {
continue
}
colNames[idx] = "?"
}
strings.Join(colNames, ", ")
}*/

View File

@ -72,7 +72,7 @@ type Table struct {
func (table *Table) ColumnStr() string { func (table *Table) ColumnStr() string {
colNames := make([]string, 0) colNames := make([]string, 0)
for _, col := range table.Columns { for _, col := range table.Columns {
colNames = append(colNames, col.Name) colNames = append(colNames, table.Name+"."+col.Name)
} }
return strings.Join(colNames, ", ") return strings.Join(colNames, ", ")
} }

View File

@ -12,6 +12,7 @@ func Create(schema string) Engine {
engine := Engine{} engine := Engine{}
engine.Mapper = SnakeMapper{} engine.Mapper = SnakeMapper{}
engine.Tables = make(map[string]Table) engine.Tables = make(map[string]Table)
engine.Statement.Engine = &engine
l := strings.Split(schema, "://") l := strings.Split(schema, "://")
if len(l) == 2 { if len(l) == 2 {
engine.Protocol = l[0] engine.Protocol = l[0]

View File

@ -33,6 +33,12 @@ type Userinfo struct {
Created time.Time Created time.Time
} }
type Userdetail struct {
Uid int `xorm:"id pk not null"`
Intro string
Profile string
}
var engine xorm.Engine var engine xorm.Engine
func directCreateTable(t *testing.T) { func directCreateTable(t *testing.T) {
@ -48,7 +54,7 @@ func mapper(t *testing.T) {
t.Error(err) t.Error(err)
} }
err = engine.Map(&Userinfo{}) err = engine.Map(&Userinfo{}, &Userdetail{})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -72,6 +78,24 @@ func insert(t *testing.T) {
} }
} }
func query(t *testing.T) {
sql := "select * from userinfo"
results, err := engine.Query(sql)
if err != nil {
t.Error(err)
}
fmt.Println(results)
}
func exec(t *testing.T) {
sql := "update userinfo set username=? where id=?"
res, err := engine.Exec(sql, "xiaolun", 1)
if err != nil {
t.Error(err)
}
fmt.Println(res)
}
func insertAutoIncr(t *testing.T) { func insertAutoIncr(t *testing.T) {
// auto increment insert // auto increment insert
user := Userinfo{Username: "xiaolunwen", Departname: "dev", Alias: "lunny", Created: time.Now()} user := Userinfo{Username: "xiaolunwen", Departname: "dev", Alias: "lunny", Created: time.Now()}
@ -90,10 +114,26 @@ func insertMulti(t *testing.T) {
} }
} }
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)
if err != nil {
t.Error(err)
}
}
func update(t *testing.T) { func update(t *testing.T) {
// update by id // update by id
user := Userinfo{Uid: 1, Username: "xxx"} user := Userinfo{Username: "xxx"}
_, err := engine.Update(&user) condiUser := Userinfo{Uid: 1}
_, err := engine.Update(&user, &condiUser)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -163,6 +203,23 @@ func order(t *testing.T) {
fmt.Println(users) fmt.Println(users)
} }
func join(t *testing.T) {
users := make([]Userinfo, 0)
err := engine.Join("LEFT", "userdetail", "userinfo.id=userdetail.id").Find(&users)
if err != nil {
t.Error(err)
}
}
func having(t *testing.T) {
users := make([]Userinfo, 0)
err := engine.GroupBy("username").Having("username='xlw'").Find(&users)
if err != nil {
t.Error(err)
}
fmt.Println(users)
}
func transaction(t *testing.T) { func transaction(t *testing.T) {
counter := func() { counter := func() {
total, err := engine.Count(&Userinfo{}) total, err := engine.Count(&Userinfo{})
@ -173,7 +230,7 @@ func transaction(t *testing.T) {
} }
counter() counter()
defer counter()
session, err := engine.MakeSession() session, err := engine.MakeSession()
defer session.Close() defer session.Close()
if err != nil { if err != nil {
@ -181,25 +238,76 @@ func transaction(t *testing.T) {
return return
} }
defer counter()
session.Begin() session.Begin()
session.IsAutoRollback = true //session.IsAutoRollback = false
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1) _, err = session.Insert(&user1)
if err != nil { if err != nil {
session.Rollback()
t.Error(err) t.Error(err)
return return
} }
user2 := Userinfo{Username: "yyy"} user2 := Userinfo{Username: "yyy"}
_, err = session.Where("id = ?", 2).Update(&user2) _, err = session.Where("uid = ?", 0).Update(&user2)
if err != nil { if err != nil {
t.Error(err) session.Rollback()
fmt.Println(err)
//t.Error(err)
return return
} }
_, err = session.Delete(&user2) _, err = session.Delete(&user2)
if err != nil { if err != nil {
session.Rollback()
t.Error(err)
return
}
err = session.Commit()
if err != nil {
t.Error(err)
return
}
}
func combineTransaction(t *testing.T) {
counter := func() {
total, err := engine.Count(&Userinfo{})
if err != nil {
t.Error(err)
}
fmt.Printf("----now total %v records\n", total)
}
counter()
defer counter()
session, err := engine.MakeSession()
defer session.Close()
if err != nil {
t.Error(err)
return
}
session.Begin()
//session.IsAutoRollback = false
user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()}
_, err = session.Insert(&user1)
if err != nil {
session.Rollback()
t.Error(err)
return
}
user2 := Userinfo{Username: "zzz"}
_, err = session.Where("id = ?", 0).Update(&user2)
if err != nil {
session.Rollback()
t.Error(err)
return
}
_, err = session.Exec("delete from userinfo where username = ?", user2.Username)
if err != nil {
session.Rollback()
t.Error(err) t.Error(err)
return return
} }
@ -218,6 +326,8 @@ func TestMysql(t *testing.T) {
directCreateTable(t) directCreateTable(t)
mapper(t) mapper(t)
insert(t) insert(t)
query(t)
exec(t)
insertAutoIncr(t) insertAutoIncr(t)
insertMulti(t) insertMulti(t)
update(t) update(t)
@ -228,7 +338,10 @@ func TestMysql(t *testing.T) {
where(t) where(t)
limit(t) limit(t)
order(t) order(t)
join(t)
having(t)
transaction(t) transaction(t)
combineTransaction(t)
} }
func TestSqlite(t *testing.T) { func TestSqlite(t *testing.T) {
@ -238,6 +351,8 @@ func TestSqlite(t *testing.T) {
directCreateTable(t) directCreateTable(t)
mapper(t) mapper(t)
insert(t) insert(t)
query(t)
exec(t)
insertAutoIncr(t) insertAutoIncr(t)
insertMulti(t) insertMulti(t)
update(t) update(t)
@ -248,5 +363,8 @@ func TestSqlite(t *testing.T) {
where(t) where(t)
limit(t) limit(t)
order(t) order(t)
join(t)
having(t)
transaction(t) transaction(t)
combineTransaction(t)
} }