2019-03-27 19:15:23 +08:00
# xorm
2019-10-17 17:26:49 +08:00
[中文 ](https://gitea.com/xorm/xorm/src/branch/master/README_CN.md )
2019-03-27 19:15:23 +08:00
Xorm is a simple and powerful ORM for Go.
2020-07-12 05:07:52 +08:00
[](https://drone.gitea.com/xorm/xorm) [](https://gocover.io/xorm.io/xorm) [](https://goreportcard.com/report/xorm.io/xorm) [](https://discord.gg/HuR2CF3)
2019-03-27 19:15:23 +08:00
2020-03-22 23:12:55 +08:00
## Notice
v1.0.0 has some break changes from v0.8.2.
- Removed some non gonic function name `Id` , `Sql` , please use `ID` , `SQL` instead.
- Removed the dependent from `xorm.io/core` and moved the codes to `xorm.io/xorm/core` , `xorm.io/xorm/names` , `xorm.io/xorm/schemas` and others.
- Renamed some interface names. i.e. `core.IMapper` -> `names.Mapper` , `core.ILogger` -> `log.Logger` .
2019-03-27 19:15:23 +08:00
## Features
* Struct <-> Table Mapping Support
* Chainable APIs
* Transaction Support
* Both ORM and raw SQL operation Support
* Sync database schema Support
* Query Cache speed up
2020-03-22 23:12:55 +08:00
* Database Reverse support via [xorm.io/reverse ](https://xorm.io/reverse )
2019-03-27 19:15:23 +08:00
* Simple cascade loading support
* Optimistic Locking support
2019-06-23 23:22:43 +08:00
* SQL Builder support via [xorm.io/builder ](https://xorm.io/builder )
2019-03-27 19:15:23 +08:00
* Automatical Read/Write seperatelly
* Postgres schema support
* Context Cache support
2020-03-22 23:12:55 +08:00
* Support log/SQLLog context
2019-03-27 19:15:23 +08:00
## Drivers Support
Drivers for Go's sql package which currently support database/sql includes:
2020-03-22 23:12:55 +08:00
* [Mysql5.* ](https://github.com/mysql/mysql-server/tree/5.7 ) / [Mysql8.* ](https://github.com/mysql/mysql-server ) / [Mariadb ](https://github.com/MariaDB/server ) / [Tidb ](https://github.com/pingcap/tidb )
- [github.com/go-sql-driver/mysql ](https://github.com/go-sql-driver/mysql )
- [github.com/ziutek/mymysql/godrv ](https://github.com/ziutek/mymysql/godrv )
2019-03-27 19:15:23 +08:00
2020-03-22 23:12:55 +08:00
* [Postgres ](https://github.com/postgres/postgres ) / [Cockroach ](https://github.com/cockroachdb/cockroach )
- [github.com/lib/pq ](https://github.com/lib/pq )
2019-03-27 19:15:23 +08:00
2020-03-22 23:12:55 +08:00
* [SQLite ](https://sqlite.org )
- [github.com/mattn/go-sqlite3 ](https://github.com/mattn/go-sqlite3 )
2019-03-27 19:15:23 +08:00
2020-03-22 23:12:55 +08:00
* MsSql
- [github.com/denisenkom/go-mssqldb ](https://github.com/denisenkom/go-mssqldb )
2019-03-27 19:15:23 +08:00
2020-03-22 23:12:55 +08:00
* Oracle
- [github.com/mattn/go-oci8 ](https://github.com/mattn/go-oci8 ) (experiment)
2019-03-27 19:15:23 +08:00
## Installation
2019-10-17 17:26:49 +08:00
go get xorm.io/xorm
2019-03-27 19:15:23 +08:00
## Documents
* [Manual ](http://xorm.io/docs )
2020-03-22 23:12:55 +08:00
* [GoDoc ](http://pkg.go.dev/xorm.io/xorm )
2019-03-27 19:15:23 +08:00
## Quick Start
* Create Engine
2020-06-16 04:46:01 +08:00
Firstly, we should new an engine for a database.
2019-03-27 19:15:23 +08:00
``` 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 ) )
```
* Create Engine Group
``` Go
dataSourceNameSlice := [ ] string { masterDataSourceName , slave1DataSourceName , slave2DataSourceName }
engineGroup , err := xorm . NewEngineGroup ( driverName , dataSourceNameSlice )
```
``` Go
masterEngine , err := xorm . NewEngine ( driverName , masterDataSourceName )
slave1Engine , err := xorm . NewEngine ( driverName , slave1DataSourceName )
slave2Engine , err := xorm . NewEngine ( driverName , slave2DataSourceName )
engineGroup , err := xorm . NewEngineGroup ( masterEngine , [ ] * Engine { slave1Engine , slave2Engine } )
```
Then all place where `engine` you can just use `engineGroup` .
* `Query` runs a SQL string, the returned results is `[]map[string][]byte` , `QueryString` returns `[]map[string]string` , `QueryInterface` returns `[]map[string]interface{}` .
``` Go
results , err := engine . Query ( "select * from user" )
results , err := engine . Where ( "a = 1" ) . Query ( )
results , err := engine . QueryString ( "select * from user" )
results , err := engine . Where ( "a = 1" ) . QueryString ( )
results , err := engine . QueryInterface ( "select * from user" )
results , err := engine . Where ( "a = 1" ) . QueryInterface ( )
```
* `Exec` runs a SQL string, it returns `affected` and `error`
``` Go
affected , err := engine . Exec ( "update user set age = ? where name = ?" , age , name )
```
* `Insert` one or multiple 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 (),(),()
```
* `Get` 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
var name string
2019-06-23 23:22:43 +08:00
has , err := engine . Table ( & user ) . Where ( "id = ?" , id ) . Cols ( "name" ) . Get ( & name )
2019-03-27 19:15:23 +08:00
// SELECT name FROM user WHERE id = ?
var id int64
2019-06-23 23:22:43 +08:00
has , err := engine . Table ( & user ) . Where ( "name = ?" , name ) . Cols ( "id" ) . Get ( & id )
2019-03-27 19:15:23 +08:00
has , err := engine . SQL ( "select id from user" ) . Get ( & id )
// SELECT id FROM user WHERE name = ?
var valuesMap = make ( map [ string ] string )
2019-06-23 23:22:43 +08:00
has , err := engine . Table ( & user ) . Where ( "id = ?" , id ) . Get ( & valuesMap )
2019-03-27 19:15:23 +08:00
// SELECT * FROM user WHERE id = ?
var valuesSlice = make ( [ ] interface { } , len ( cols ) )
2019-06-23 23:22:43 +08:00
has , err := engine . Table ( & user ) . Where ( "id = ?" , id ) . Cols ( cols ... ) . Get ( & valuesSlice )
2019-03-27 19:15:23 +08:00
// SELECT col1, col2, col3 FROM user WHERE id = ?
```
* `Exist` check if one record exist on table
``` Go
has , err := testEngine . Exist ( new ( RecordExist ) )
// SELECT * FROM record_exist LIMIT 1
has , err = testEngine . Exist ( & RecordExist {
Name : "test1" ,
} )
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has , err = testEngine . Where ( "name = ?" , "test1" ) . Exist ( & RecordExist { } )
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has , err = testEngine . SQL ( "select * from record_exist where name = ?" , "test1" ) . Exist ( )
// select * from record_exist where name = ?
has , err = testEngine . Table ( "record_exist" ) . Exist ( )
// SELECT * FROM record_exist LIMIT 1
has , err = testEngine . Table ( "record_exist" ) . Where ( "name = ?" , "test1" ) . Exist ( )
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
```
* `Find` 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 10 offset 0
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 10 offset 0
```
* `Iterate` and `Rows` query multiple records and record by record handle, there are 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
err := engine . BufferSize ( 100 ) . Iterate ( & User { Name : name } , func ( idx int , bean interface { } ) error {
user := bean . ( * User )
return nil
} )
// SELECT * FROM user Limit 0, 100
// SELECT * FROM user Limit 101, 100
rows , err := engine . Rows ( & User { Name : name } )
// SELECT * FROM user
defer rows . Close ( )
bean := new ( Struct )
for rows . Next ( ) {
err = rows . Scan ( bean )
}
```
* `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on.
``` 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 ( "id" , 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` delete one or more records, Delete MUST have condition
``` Go
affected , err := engine . Where ( ... ) . Delete ( & user )
// DELETE FROM user Where ...
affected , err := engine . ID ( 2 ) . Delete ( & user )
// DELETE FROM user Where id = ?
```
* `Count` count records
``` Go
counts , err := engine . Count ( & user )
// SELECT count(*) AS total FROM user
```
2019-08-26 23:17:23 -03:00
* `FindAndCount` combines function `Find` with `Count` which is usually used in query by page
``` Go
var users [ ] User
counts , err := engine . FindAndCount ( & users )
```
2019-03-27 19:15:23 +08:00
* `Sum` sum functions
``` Go
agesFloat64 , err := engine . Sum ( & user , "age" )
// SELECT sum(age) AS total FROM user
agesInt64 , err := engine . SumInt ( & user , "age" )
// SELECT sum(age) AS total FROM user
sumFloat64Slice , err := engine . Sums ( & user , "age" , "score" )
// SELECT sum(age), sum(score) FROM user
sumInt64Slice , err := engine . SumsInt ( & user , "age" , "score" )
// SELECT sum(age), sum(score) FROM user
```
* Query conditions builder
``` Go
err := engine . Where ( builder . NotIn ( "a" , 1 , 2 ) . And ( builder . In ( "b" , "c" , "d" , "e" ) ) ) . Find ( & users )
// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
```
* Multiple operations in one go routine, no transation here but resue session memory
``` Go
session := engine . NewSession ( )
defer session . Close ( )
user1 := Userinfo { Username : "xiaoxiao" , Departname : "dev" , Alias : "lunny" , Created : time . Now ( ) }
if _ , err := session . Insert ( & user1 ) ; err != nil {
return err
}
user2 := Userinfo { Username : "yyy" }
if _ , err := session . Where ( "id = ?" , 2 ) . Update ( & user2 ) ; err != nil {
return err
}
if _ , err := session . Exec ( "delete from userinfo where username = ?" , user2 . Username ) ; err != nil {
return err
}
return nil
```
2019-10-17 17:26:49 +08:00
* Transation should be on one go routine. There is transaction and resue session memory
2019-03-27 19:15:23 +08:00
``` Go
session := engine . NewSession ( )
defer session . Close ( )
// add Begin() before any action
if err := session . Begin ( ) ; err != nil {
// if returned then will rollback automatically
return err
}
user1 := Userinfo { Username : "xiaoxiao" , Departname : "dev" , Alias : "lunny" , Created : time . Now ( ) }
if _ , err := session . Insert ( & user1 ) ; err != nil {
return err
}
user2 := Userinfo { Username : "yyy" }
if _ , err := session . Where ( "id = ?" , 2 ) . Update ( & user2 ) ; err != nil {
return err
}
if _ , err := session . Exec ( "delete from userinfo where username = ?" , user2 . Username ) ; err != nil {
return err
}
// add Commit() after all actions
return session . Commit ( )
```
* Or you can use `Transaction` to replace above codes.
``` Go
2019-06-23 23:22:43 +08:00
res , err := engine . Transaction ( func ( session * xorm . Session ) ( interface { } , error ) {
2019-03-27 19:15:23 +08:00
user1 := Userinfo { Username : "xiaoxiao" , Departname : "dev" , Alias : "lunny" , Created : time . Now ( ) }
if _ , err := session . Insert ( & user1 ) ; err != nil {
return nil , err
}
user2 := Userinfo { Username : "yyy" }
if _ , err := session . Where ( "id = ?" , 2 ) . Update ( & user2 ) ; err != nil {
return nil , err
}
if _ , err := session . Exec ( "delete from userinfo where username = ?" , user2 . Username ) ; err != nil {
return nil , err
}
return nil , nil
} )
```
* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
``` Go
sess := engine . NewSession ( )
defer sess . Close ( )
var context = xorm . NewMemoryContextCache ( )
var c2 ContextGetStruct
has , err := sess . ID ( 1 ) . ContextCache ( context ) . Get ( & c2 )
assert . NoError ( t , err )
assert . True ( t , has )
assert . EqualValues ( t , 1 , c2 . Id )
assert . EqualValues ( t , "1" , c2 . Name )
sql , args := sess . LastSQL ( )
assert . True ( t , len ( sql ) > 0 )
assert . True ( t , len ( args ) > 0 )
var c3 ContextGetStruct
has , err = sess . ID ( 1 ) . ContextCache ( context ) . Get ( & c3 )
assert . NoError ( t , err )
assert . True ( t , has )
assert . EqualValues ( t , 1 , c3 . Id )
assert . EqualValues ( t , "1" , c3 . Name )
sql , args = sess . LastSQL ( )
assert . True ( t , len ( sql ) == 0 )
assert . True ( t , len ( args ) == 0 )
```
## Contributing
2020-06-16 04:46:01 +08:00
If you want to pull request, please see [CONTRIBUTING ](https://gitea.com/xorm/xorm/src/branch/master/CONTRIBUTING.md ). And you can also go to [Xorm on discourse ](https://xorm.discourse.group ) to discuss.
2019-03-27 19:15:23 +08:00
## Credits
### Contributors
This project exists thanks to all the people who contribute. [[Contribute ](CONTRIBUTING.md )].
<a href="graphs/contributors"><img src="https://opencollective.com/xorm/contributors.svg?width=890&button=false" /></a>
### Backers
Thank you to all our backers! 🙏 [[Become a backer ](https://opencollective.com/xorm#backer )]
<a href="https://opencollective.com/xorm#backers " target="_blank"><img src="https://opencollective.com/xorm/backers.svg?width=890"></a>
### Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor ](https://opencollective.com/xorm#sponsor )]
## Changelog
2020-03-22 23:12:55 +08:00
You can find all the changelog [here ](CHANGELOG.md )
2019-03-27 19:15:23 +08:00
## Cases
* [studygolang ](http://studygolang.com/ ) - [github.com/studygolang/studygolang ](https://github.com/studygolang/studygolang )
* [Gitea ](http://gitea.io ) - [github.com/go-gitea/gitea ](http://github.com/go-gitea/gitea )
* [Gogs ](http://try.gogits.org ) - [github.com/gogits/gogs ](http://github.com/gogits/gogs )
* [grafana ](https://grafana.com/ ) - [github.com/grafana/grafana ](http://github.com/grafana/grafana )
* [github.com/m3ng9i/qreader ](https://github.com/m3ng9i/qreader )
* [Wego ](http://github.com/go-tango/wego )
* [Docker.cn ](https://docker.cn/ )
* [Xorm Adapter ](https://github.com/casbin/xorm-adapter ) for [Casbin ](https://github.com/casbin/casbin ) - [github.com/casbin/xorm-adapter ](https://github.com/casbin/xorm-adapter )
* [Gorevel ](http://gorevel.cn/ ) - [github.com/goofcc/gorevel ](http://github.com/goofcc/gorevel )
* [Gowalker ](http://gowalker.org ) - [github.com/Unknwon/gowalker ](http://github.com/Unknwon/gowalker )
* [Gobuild.io ](http://gobuild.io ) - [github.com/shxsun/gobuild ](http://github.com/shxsun/gobuild )
* [Sudo China ](http://sudochina.com ) - [github.com/insionng/toropress ](http://github.com/insionng/toropress )
* [Godaily ](http://godaily.org ) - [github.com/govc/godaily ](http://github.com/govc/godaily )
* [YouGam ](http://www.yougam.com/ )
* [GoCMS - github.com/zzboy/GoCMS ](https://github.com/zzdboy/GoCMS )
* [GoBBS - gobbs.domolo.com ](http://gobbs.domolo.com/ )
* [go-blog ](http://wangcheng.me ) - [github.com/easykoo/go-blog ](https://github.com/easykoo/go-blog )
## LICENSE
2019-06-23 23:22:43 +08:00
BSD License [http://creativecommons.org/licenses/BSD/ ](http://creativecommons.org/licenses/BSD/ )