xorm
xorm是一個簡單而強大的ORM庫。
安裝
go get -u github.com/go-xorm/xorm
驅動支持
Mysql: github.com/go-sql-driver/mysql MyMysql: github.com/ziutek/mymysql Postgres: github.com/lib/pq Tidb: github.com/pingcap/tidb SQLite: github.com/mattn/go-sqlite3 MsSql: github.com/denisenkom/go-mssqldb MsSql: github.com/lunny/godbc Oracle: github.com/mattn/go-oci8 (試驗性支持)
創建orm引擎
一個xorm可同時存在orm引擎,一個Orm引擎稱為Engine,一個Engine一般只對應一個數據庫。Engine通過調用xorm.NewEngine生成,如:
package main import ( "github.com/go-xorm/xorm" _ "github.com/go-sql-driver/mysql" ) func main() { engine, err := xorm.NewEngine("mysql", "root:passwd@tcp(127.0.0.1:3306)/dabase_name?timeout=3s&parseTime=true&loc=Local&charset=utf8") if err !=nil{ return } engine.Ping() // 可以判斷是否能連接 defer engine.Close() // 退出后關閉 }
定義表結構體
表名映射一般有三種方式,且都按優先級高低
-
表名的優先級順序如下:
- engine.Table() 指定的臨時表名優先級最高
- TableName() string 其次
- Mapper 自動映射的表名優先級最后
-
字段名的優先級順序如下:
- 結構體tag指定的字段名優先級較高
- Mapper 自動映射的表名優先級較低
Column屬性定義,首先定義一個結構體如
type User struct { Id int64 Name string `xorm:"varchar(25) notnull unique 'usr_name'"` Balance float64 Version int `xorm:"version"` // 樂觀鎖 }
具體tag詳情如下。且字段名根據不同數據庫區分大小寫
Tag | 說明 |
---|---|
name | 當前field對應的字段的名稱,可選,如不寫,則自動根據field名字和轉換規則命名,如與其它關鍵字沖突,請使用單引號括起來。 |
pk | 是否是Primary Key,如果在一個struct中有多個字段都使用了此標記,則這多個字段構成了復合主鍵,單主鍵當前支持int32,int,int64,uint32,uint,uint64,string這7種Go的數據類型,復合主鍵支持這7種Go的數據類型的組合。 |
當前支持30多種字段類型,詳情參見本文最后一個表格 | 字段類型 |
autoincr | 是否是自增 |
[not ]null 或 notnull | 是否可以為空 |
unique或unique(uniquename) | 是否是唯一,如不加括號則該字段不允許重復;如加上括號,則括號中為聯合唯一索引的名字,此時如果有另外一個或多個字段和本unique的uniquename相同,則這些uniquename相同的字段組成聯合唯一索引 |
index或index(indexname) | 是否是索引,如不加括號則該字段自身為索引,如加上括號,則括號中為聯合索引的名字,此時如果有另外一個或多個字段和本index的indexname相同,則這些indexname相同的字段組成聯合索引 |
extends | 應用於一個匿名成員結構體或者非匿名成員結構體之上,表示此結構體的所有成員也映射到數據庫中,extends可加載無限級 |
- | 這個Field將不進行字段映射 |
-> | 這個Field將只寫入到數據庫而不從數據庫讀取 |
<- | 這個Field將只從數據庫讀取,而不寫入到數據庫 |
created | 這個Field將在Insert時自動賦值為當前時間 |
updated | 這個Field將在Insert或Update時自動賦值為當前時間 |
deleted | 這個Field將在Delete時設置為當前時間,並且當前記錄不刪除 |
version | 這個Field將會在insert時默認為1,每次更新自動加1 |
default 0或default(0) | 設置默認值,緊跟的內容如果是Varchar等需要加上單引號 |
json | 表示內容將先轉成Json格式,然后存儲到數據庫中,數據庫中的字段類型可以為Text或者二進制 |
需要注意的幾點
- 如果field名稱為ID,且類型為int64,並且 沒有定義tag,則會被xorm視為主鍵,且擁有自增屬性。如果要用其它名字為主鍵,需對應tag加上 xorm:"pk"
- string類型默認為varchar(255)
- 支持type MyString string 等自定義的field。支持Slice, Map,等field成員。這些成員默認存儲為Text類型。並且擁有Json格式來序列化和反序列化。
- 實現Conversion接口的類型或者結構體,將根據接口的轉換方式在類型和數據庫記錄之間進行相互轉換。
type Conversion interface { FromDB([]byte) error ToDB() ([]byte, error) }
表結構常用操作
獲取數據庫信息
- DBMetas(): xorm支持獲取表結構信息。通過調用engine.DBMetas()獲取表,字段,索引信息
- TableInfo(): 根據傳入的結構體指針及對應的Tag,提取出模型對應的表結構信息。
表操作
- CreateTables(): 創建表engine.CreateTables() 參數為一個或多個空的對應Struct的指針。可用方法有Charset()和StoreEngine()。
- IsTableEmpty(): 判斷是否為空。參數和CreateTables()相同。
- IsTableExist():判斷是否存在
- DropTables(): 刪除表engine.DropTables().參數為一個或多個空的對應Struct的指針或者表的名字。如果為string傳入,則只刪除對應的表,如果傳入的為Struct,則刪除表的同時還會刪除對應的索引。
創建索引和唯一索引
- CreateIndexes: 根據struct中的Tag來創建索引
- CreateUniques: 根據struct中的tag來創建唯一索引
同步數據庫結構到 mysql中
- Sync
- 自動檢測和創建表,這個檢測是根據表的名字
- 自動檢測和新增表中的字段,這個檢測是根據字段名
- 自動檢測和創建索引和唯一索引,這個檢測是根據索引的一個或多個字段名,而不根據索引名稱
err := engine.Sync(new(User), new(Group)) // 其中User ,Group為要創建的兩個表對應的struct
-
Sync2, 對Sync進行改進,推薦使用Sync2
- 自動檢測和創建表,這個檢測是根據表的名字
- 自動檢測和新增表中的字段,這個檢測是根據字段名,同時對表中多余的字段給出警告信息
- 自動檢測,創建和刪除索引和唯一索引,這個檢測是根據索引的一個或多個字段名,而不根據索引名稱。因此這里需要注意,如果在一個有大量數據的表中引入新的索引,數據庫可能需要一定的時間來建立索引。
- 自動轉換varchar字段類型到text字段類型,自動警告其它字段類型在模型和數據庫之間不一致的情況。
- 自動警告字段的默認值,是否為空信息在模型和數據庫之間不匹配的情況
以上信息需要將engine.ShowWarn設置為true才會顯示。調用方法
err := engine.Sync2(new(User), new(Group))
導入導出SQL腳本
dump
engine.DumpAll(w io.Writer) 或 engine.DumpAllFile(fpath string)
Import
engine.Import(r io.Reader) 或者 engine.ImportFile(fpath string)
插入數據操作(表結構參照上面User表)
ORM插入一條數據
user := new(User) user.Name = "myname" affected, err := engine.Insert(user) // INSERT INTO user (name) values (?)
插入同一個表的多條數據
users := make([]User, 1) users[0].Name = "name0" users[0].ID = "0" ... affected, err := engine.Insert(&users)
指針Slice插入多條記錄
users := make([]*User, 1) users[0] = new(User) users[0].Name = "name0" users[0].ID = "0" ... affected, err := engine.Insert(&users)
不同表的一條記錄
user := new(User) user.Name = "myname" question := new(Question) question.Content = "whywhywhwy?" affected, err := engine.Insert(user, question)
不同表的多條記錄
users := make([]User, 1) users[0].Name = "name0" ... questions := make([]Question, 1) questions[0].Content = "whywhywhwy?" affected, err := engine.Insert(&users, &questions)
使用SQL插入數據
sql ="insert into config(key,value) values (?, ?)" res, err := engine.Exec(sql, "OSCHINA", "OSCHINA") 或者 sql_2 := "insert into config(key,value) values (?, ?)" affected, err := engine.Sql(sql_4, "OSCHINA", "OSCHINA").Execute() 或者 //SqlMap中key為 "sql_i_1" 配置的Sql語句為:insert into config(key,value) values (?, ?) sql_i_1 := "sql_i_1" affected, err := engine.SqlMapClient(sql_i_1, "config_1", "1").Execute() 或者 sql_i_3 := "insert.example.stpl" paramMap_i_t := map[string]interface{}{"key": "config_3", "value": "3"} affected, err := engine.SqlTemplateClient(sql_i_3, ¶mMap_i_t).Execute()
查詢操作
ORM常用查詢
注意下面出現的 & +表結構體
設置別名
engine.Alias("o").Where("o.name = ?", name).Get(&order)
條件查找
engine.Where(...).And(...).Get(&order)
某個字段排序
// 正序 engine.Asc("id").Find(&orders) //倒序 engine.Asc("id").Desc("time").Find(&orders)
主鍵查找
var user User engine.Id(1).Get(&user) // SELECT * FROM user Where id = 1 // 或者復合主鍵 engine.Id(core.PK{1, "name"}).Get(&user) // SELECT * FROM user Where id =1 AND name= 'name'
select, in, cols
engine.Select("a.*, (select name from b limit 1) as name").Find(&beans) engine.Select("a.*, (select name from b limit 1) as name").Get(&bean) // in engine.In("cloumn", 1, 2, 3).Find() engine.In("column", []int{1, 2, 3}).Find() // cols engine.Cols("age", "name").Get(&usr) // SELECT age, name FROM user limit 1 engine.Cols("age", "name").Find(&users) // SELECT age, name FROM user engine.Cols("age", "name").Update(&user) // UPDATE user SET age=? AND name=?
GET方法,查詢到的數據會賦給結構體
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 has, err := engine.Where("id = ?", id).Cols("name").Get(&name) // SELECT name FROM user WHERE id = ? var id int64 has, err := engine.Where("name = ?", name).Cols("id").Get(&id) // SELECT id FROM user WHERE name = ? var valuesMap = make(map[string]string) has, err := engine.Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? var valuesSlice = make([]interface{}, len(cols)) has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) // SELECT col1, col2, col3 FROM user WHERE id = ? 或者 user := new(User) has, err := engine.Where("name=?", "xlw").Get(user)
子查詢
var student []Student err = db.Table("student").Select("id ,name").Where("id in (?)", db.Table("studentinfo").Select("id").Where("status = ?", 2).QueryExpr()).Find(&student) //SELECT id ,name FROM `student` WHERE (id in (SELECT id FROM `studentinfo` WHERE (status = 2)))
SQL操作返回格式json or xml
var users []User results,err := engine.Where("id=?", 6).Search(&users).Xml() //返回查詢結果的xml字符串 results,err := engine.Where("id=?", 6).Search(&users).Json() //返回查詢結果的json字符串
更新操作
update方法
user := new(User) user.Name = "myname" affected, err := engine.Id(id).Update(user)
指定更新值
affected, err := engine.Id(id).Cols("age").Update(&user) // 或 affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
更新時間,可以在字段名后添加 update如下
type User struct { Id int64 Name string UpdatedAt time.Time `xorm:"updated"` }
刪除操作
delete方法
user := new(User) affected, err := engine.Id(id).Delete(user) //Delete的返回值第一個參數為刪除的記錄數,第二個參數為錯誤。
xorm還提供了軟刪除,如下設置
type User struct { Id int64 Name string DeletedAt time.Time `xorm:"deleted"` }
如果設置軟刪除,那么永久刪除或者獲取使用Unscoped
var user User engine.Id(1).Unscoped().Get(&user) // 此時將可以獲得記錄 engine.Id(1).Unscoped().Delete(&user) // 此時將可以真正的刪除記錄
創建數據庫組
xorm提供了可以連接多個數據庫。如下
package main import ( "github.com/go-xorm/xorm" _ "github.com/go-sql-driver/mysql" ) func main() { conns := []string{ "mysql", "root:passwd@tcp(127.0.0.1:3306)/dabase_name?timeout=3s&parseTime=true&loc=Local&charset=utf8", "mysql", "root:passwd@tcp(127.0.0.1:3306)/dabase_name?timeout=3s&parseTime=true&loc=Local&charset=utf8", "mysql", "root:passwd@tcp(127.0.0.1:3306)/dabase_name?timeout=3s&parseTime=true&loc=Local&charset=utf8", } engine, err := xorm.NewEngineGroup("mysql", conns) if err !=nil{ return } engine.Ping() // 可以判斷是否能連接 defer engine.Close() // 退出后關閉 }
連接池
engine內部支持連接池接口和對應的函數。
- 如果需要設置連接池的空閑數大小,可以使用engine.SetMaxIdleConns()來實現。
- 如果需要設置最大打開連接數,則可以使用engine.SetMaxOpenConns()來實現。
詳細查詢操作可參照github,鏈接 https://github.com/go-xorm/xorm/