5.5 使用Beego orm库进行ORM开发

beego orm是我开发的一个Go进行ORM操作的库,它采用了Go style方式对数据库进行操作,实现了struct到数据表记录的映射。beego orm是一个十分轻量级的Go ORM框架,开发这个库的本意降低复杂的ORM学习曲线,尽可能在ORM的运行效率和功能之间寻求一个平衡,beego orm是目前开源的Go ORM框架中实现比较完整的一个库,而且运行效率相当不错,功能也基本能满足需求。

beego orm是支持database/sql标准接口的ORM库,所以理论上来说,只要数据库驱动支持database/sql接口就可以无缝的接入beego orm。目前我测试过的驱动包括下面几个:

Mysql: github/go-mysql-driver/mysql

PostgreSQL: github.com/lib/pq

SQLite: github.com/mattn/go-sqlite3

Mysql: github.com/ziutek/mymysql/godrv

暂未支持数据库:

MsSql: github.com/denisenkom/go-mssqldb

MS ADODB: github.com/mattn/go-adodb

Oracle: github.com/mattn/go-oci8

ODBC: bitbucket.org/miquella/mgodbc

安装

beego orm支持go get方式安装,是完全按照Go Style的方式来实现的。

  1. go get github.com/astaxie/beego

如何初始化

首先你需要import相应的数据库驱动包、database/sql标准接口包以及beego orm包,如下所示:

  1. import (
  2. "database/sql"
  3. "github.com/astaxie/beego/orm"
  4. _ "github.com/go-sql-driver/mysql"
  5. )
  6. func init() {
  7. // 设置默认数据库
  8. orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
  9. //注册定义的model
  10. orm.RegisterModel(new(User))
  11. // 创建table
  12. orm.RunSyncdb("default", false, true)
  13. }

PostgreSQL 配置:

  1. //导入驱动
  2. // _ "github.com/lib/pq"
  3. // 注册驱动
  4. orm.RegisterDriver("postgres", orm.DR_Postgres)
  5. // 设置默认数据库
  6. //PostgresQL用户:postgres ,密码:zxxx , 数据库名称:test , 数据库别名:default
  7. orm.RegisterDataBase("default", "postgres", "user=postgres password=zxxx dbname=test host=127.0.0.1 port=5432 sslmode=disable")

MySQL 配置:

  1. //导入驱动
  2. //_ "github.com/go-sql-driver/mysql"
  3. //注册驱动
  4. orm.RegisterDriver("mysql", orm.DR_MySQL)
  5. // 设置默认数据库
  6. //mysql用户:root ,密码:zxxx , 数据库名称:test , 数据库别名:default
  7. orm.RegisterDataBase("default", "mysql", "root:zxxx@/test?charset=utf8")

Sqlite 配置:

  1. //导入驱动
  2. //_ "github.com/mattn/go-sqlite3"
  3. //注册驱动
  4. orm.RegisterDriver("sqlite", orm.DR_Sqlite)
  5. // 设置默认数据库
  6. //数据库存放位置:./datas/test.db , 数据库别名:default
  7. orm.RegisterDataBase("default", "sqlite3", "./datas/test.db")

导入必须的package之后,我们需要打开到数据库的链接,然后创建一个beego orm对象(以MySQL为例),如下所示 beego orm:

  1. func main() {
  2. orm := orm.NewOrm()
  3. }

简单示例:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/astaxie/beego/orm"
  5. _ "github.com/go-sql-driver/mysql" // 导入数据库驱动
  6. )
  7. // Model Struct
  8. type User struct {
  9. Id int
  10. Name string `orm:"size(100)"`
  11. }
  12. func init() {
  13. // 设置默认数据库
  14. orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
  15. // 注册定义的 model
  16. orm.RegisterModel(new(User))
  17. //RegisterModel 也可以同时注册多个 model
  18. //orm.RegisterModel(new(User), new(Profile), new(Post))
  19. // 创建 table
  20. orm.RunSyncdb("default", false, true)
  21. }
  22. func main() {
  23. o := orm.NewOrm()
  24. user := User{Name: "slene"}
  25. // 插入表
  26. id, err := o.Insert(&user)
  27. fmt.Printf("ID: %d, ERR: %v\n", id, err)
  28. // 更新表
  29. user.Name = "astaxie"
  30. num, err := o.Update(&user)
  31. fmt.Printf("NUM: %d, ERR: %v\n", num, err)
  32. // 读取 one
  33. u := User{Id: user.Id}
  34. err = o.Read(&u)
  35. fmt.Printf("ERR: %v\n", err)
  36. // 删除表
  37. num, err = o.Delete(&u)
  38. fmt.Printf("NUM: %d, ERR: %v\n", num, err)
  39. }

SetMaxIdleConns

根据数据库的别名,设置数据库的最大空闲连接

  1. orm.SetMaxIdleConns("default", 30)

SetMaxOpenConns

根据数据库的别名,设置数据库的最大数据库连接 (go >= 1.2)

  1. orm.SetMaxOpenConns("default", 30)

目前beego orm支持打印调试,你可以通过如下的代码实现调试

  1. orm.Debug = true

接下来我们的例子采用前面的数据库表User,现在我们建立相应的struct

  1. type Userinfo struct {
  2. Uid int `PK` //如果表的主键不是id,那么需要加上pk注释,显式的说这个字段是主键
  3. Username string
  4. Departname string
  5. Created time.Time
  6. }
  7. type User struct {
  8. Uid int `PK` //如果表的主键不是id,那么需要加上pk注释,显式的说这个字段是主键
  9. Name string
  10. Profile *Profile `orm:"rel(one)"` // OneToOne relation
  11. Post []*Post `orm:"reverse(many)"` // 设置一对多的反向关系
  12. }
  13. type Profile struct {
  14. Id int
  15. Age int16
  16. User *User `orm:"reverse(one)"` // 设置一对一反向关系(可选)
  17. }
  18. type Post struct {
  19. Id int
  20. Title string
  21. User *User `orm:"rel(fk)"` //设置一对多关系
  22. Tags []*Tag `orm:"rel(m2m)"`
  23. }
  24. type Tag struct {
  25. Id int
  26. Name string
  27. Posts []*Post `orm:"reverse(many)"`
  28. }
  29. func init() {
  30. // 需要在init中注册定义的model
  31. orm.RegisterModel(new(Userinfo),new(User), new(Profile), new(Tag))
  32. }

注意一点,beego orm针对驼峰命名会自动帮你转化成下划线字段,例如你定义了Struct名字为UserInfo,那么转化成底层实现的时候是user_info,字段命名也遵循该规则。

插入数据

下面的代码演示了如何插入一条记录,可以看到我们操作的是struct对象,而不是原生的sql语句,最后通过调用Insert接口将数据保存到数据库。

  1. o := orm.NewOrm()
  2. var user User
  3. user.Name = "zxxx"
  4. user.Departname = "zxxx"
  5. id, err := o.Insert(&user)
  6. if err == nil {
  7. fmt.Println(id)
  8. }

我们看到插入之后user.Uid就是插入成功之后的自增ID。

同时插入多个对象:InsertMulti

类似sql语句

  1. insert into table (name, age) values("slene", 28),("astaxie", 30),("unknown", 20)

第一个参数 bulk 为并列插入的数量,第二个为对象的slice

返回值为成功插入的数量

  1. users := []User{
  2. {Name: "slene"},
  3. {Name: "astaxie"},
  4. {Name: "unknown"},
  5. ...
  6. }
  7. successNums, err := o.InsertMulti(100, users)

bulk 为 1 时,将会顺序插入 slice 中的数据

更新数据

继续上面的例子来演示更新操作,现在user的主键已经有值了,此时调用Insert接口,beego orm内部会自动调用update以进行数据的更新而非插入操作。

  1. o := orm.NewOrm()
  2. user := User{Uid: 1}
  3. if o.Read(&user) == nil {
  4. user.Name = "MyName"
  5. if num, err := o.Update(&user); err == nil {
  6. fmt.Println(num)
  7. }
  8. }

Update 默认更新所有的字段,可以更新指定的字段:

  1. // 只更新 Name
  2. o.Update(&user, "Name")
  3. // 指定多个字段
  4. // o.Update(&user, "Field1", "Field2", ...)

//Where:用来设置条件,支持多个参数,第一个参数如果为整数,相当于调用了Where(“主键=?”,值)。

查询数据

beego orm的查询接口比较灵活,具体使用请看下面的例子

例子1,根据主键获取数据:

  1. o := orm.NewOrm()
  2. var user User
  3. user := User{Id: 1}
  4. err = o.Read(&user)
  5. if err == orm.ErrNoRows {
  6. fmt.Println("查询不到")
  7. } else if err == orm.ErrMissPK {
  8. fmt.Println("找不到主键")
  9. } else {
  10. fmt.Println(user.Id, user.Name)
  11. }

例子2:

  1. o := orm.NewOrm()
  2. var user User
  3. qs := o.QueryTable(user) // 返回 QuerySeter
  4. qs.Filter("id", 1) // WHERE id = 1
  5. qs.Filter("profile__age", 18) // WHERE profile.age = 18

例子3,WHERE IN查询条件:

  1. qs.Filter("profile__age__in", 18, 20)
  2. // WHERE profile.age IN (18, 20)

例子4,更加复杂的条件:

  1. qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
  2. // WHERE profile.age IN (18, 20) AND NOT profile_id < 1000

可以通过如下接口获取多条数据,请看示例

例子1,根据条件age>17,获取20位置开始的10条数据的数据

  1. var allusers []User
  2. qs.Filter("profile__age__gt", 17)
  3. // WHERE profile.age > 17

例子2,limit默认从10开始,获取10条数据

  1. qs.Limit(10, 20)
  2. // LIMIT 10 OFFSET 20 注意跟SQL反过来的

删除数据

beedb提供了丰富的删除数据接口,请看下面的例子

例子1,删除单条数据

  1. o := orm.NewOrm()
  2. if num, err := o.Delete(&User{Id: 1}); err == nil {
  3. fmt.Println(num)
  4. }

Delete 操作会对反向关系进行操作,此例中 Post 拥有一个到 User 的外键。删除 User 的时候。如果 on_delete 设置为默认的级联操作,将删除对应的 Post

关联查询

有些应用却需要用到连接查询,所以现在beego orm提供了一个简陋的实现方案:

  1. type Post struct {
  2. Id int `orm:"auto"`
  3. Title string `orm:"size(100)"`
  4. User *User `orm:"rel(fk)"`
  5. }
  6. var posts []*Post
  7. qs := o.QueryTable("post")
  8. num, err := qs.Filter("User__Name", "slene").All(&posts)

上面代码中我们看到了一个struct关联查询

Group By和Having

针对有些应用需要用到group by的功能,beego orm也提供了一个简陋的实现

  1. qs.OrderBy("id", "-profile__age")
  2. // ORDER BY id ASC, profile.age DESC
  3. qs.OrderBy("-profile__age", "profile")
  4. // ORDER BY profile.age DESC, profile_id ASC

上面的代码中出现了两个新接口函数

GroupBy:用来指定进行groupby的字段

Having:用来指定having执行的时候的条件

使用原生sql

简单示例:

  1. o := NewOrm()
  2. var r RawSeter
  3. r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")

复杂原生sql使用:

  1. func (m *User) Query(name string) []User {
  2. var o orm.Ormer
  3. var rs orm.RawSeter
  4. o = orm.NewOrm()
  5. rs = o.Raw("SELECT * FROM user "+
  6. "WHERE name=? AND uid>10 "+
  7. "ORDER BY uid DESC "+
  8. "LIMIT 100", name)
  9. var user []User
  10. num, err := rs.QueryRows(&user)
  11. if err != nil {
  12. fmt.Println(err)
  13. } else {
  14. fmt.Println(num)
  15. return user
  16. }
  17. }

更多说明,请到beego.me

进一步的发展

目前beego orm已经获得了很多来自国内外用户的反馈,我目前也正在考虑支持更多数据库,接下来会在更多方面进行改进