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/