Files
build-web-application-with-…/zh/05.5.md
2016-12-27 20:07:27 +08:00

446 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://github.com/go-sql-driver/mysql)
PostgreSQL: [github.com/bmizerany/pq](https://github.com/lib/pq)
SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
Mysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql)
暂未支持数据库:
MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
MS ADODB: [github.com/mattn/go-adodb](https://github.com/mattn/go-adodb)
Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8)
ODBC: [bitbucket.org/miquella/mgodbc](https://bitbucket.org/miquella/mgodbc)
## 安装
beego orm支持go get方式安装是完全按照Go Style的方式来实现的。
go get github.com/astaxie/beego
## 如何初始化
首先你需要import相应的数据库驱动包、database/sql标准接口包以及beego orm包如下所示
```Go
import (
"database/sql"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
// 设置默认数据库
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
//注册定义的model
orm.RegisterModel(new(User))
// 创建table
orm.RunSyncdb("default", false, true)
}
```
PostgreSQL 配置:
```Go
//导入驱动
// _ "github.com/lib/pq"
// 注册驱动
orm.RegisterDriver("postgres", orm.DR_Postgres)
// 设置默认数据库
//PostgresQL用户postgres 密码zxxx 数据库名称test 数据库别名default
orm.RegisterDataBase("default", "postgres", "user=postgres password=zxxx dbname=test host=127.0.0.1 port=5432 sslmode=disable")
```
MySQL 配置:
```Go
//导入驱动
//_ "github.com/go-sql-driver/mysql"
//注册驱动
orm.RegisterDriver("mysql", orm.DR_MySQL)
// 设置默认数据库
//mysql用户root 密码zxxx 数据库名称test 数据库别名default
orm.RegisterDataBase("default", "mysql", "root:zxxx@/test?charset=utf8")
```
Sqlite 配置:
```Go
//导入驱动
//_ "github.com/mattn/go-sqlite3"
//注册驱动
orm.RegisterDriver("sqlite", orm.DR_Sqlite)
// 设置默认数据库
//数据库存放位置:./datas/test.db 数据库别名default
orm.RegisterDataBase("default", "sqlite3", "./datas/test.db")
```
导入必须的package之后,我们需要打开到数据库的链接然后创建一个beego orm对象以MySQL为例),如下所示
beego orm:
```Go
func main() {
orm := orm.NewOrm()
}
```
简单示例:
```Go
package main
import (
"fmt"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql" // 导入数据库驱动
)
// Model Struct
type User struct {
Id int
Name string `orm:"size(100)"`
}
func init() {
// 设置默认数据库
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
// 注册定义的 model
orm.RegisterModel(new(User))
//RegisterModel 也可以同时注册多个 model
//orm.RegisterModel(new(User), new(Profile), new(Post))
// 创建 table
orm.RunSyncdb("default", false, true)
}
func main() {
o := orm.NewOrm()
user := User{Name: "slene"}
// 插入表
id, err := o.Insert(&user)
fmt.Printf("ID: %d, ERR: %v\n", id, err)
// 更新表
user.Name = "astaxie"
num, err := o.Update(&user)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
// 读取 one
u := User{Id: user.Id}
err = o.Read(&u)
fmt.Printf("ERR: %v\n", err)
// 删除表
num, err = o.Delete(&u)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
}
```
SetMaxIdleConns
根据数据库的别名,设置数据库的最大空闲连接
```Go
orm.SetMaxIdleConns("default", 30)
```
SetMaxOpenConns
根据数据库的别名,设置数据库的最大数据库连接 (go >= 1.2)
```Go
orm.SetMaxOpenConns("default", 30)
```
目前beego orm支持打印调试你可以通过如下的代码实现调试
```Go
orm.Debug = true
```
接下来我们的例子采用前面的数据库表User现在我们建立相应的struct
```Go
type Userinfo struct {
Uid int `PK` //如果表的主键不是id那么需要加上pk注释显式的说这个字段是主键
Username string
Departname string
Created time.Time
}
type User struct {
Uid int `PK` //如果表的主键不是id那么需要加上pk注释显式的说这个字段是主键
Name string
Profile *Profile `orm:"rel(one)"` // OneToOne relation
Post []*Post `orm:"reverse(many)"` // 设置一对多的反向关系
}
type Profile struct {
Id int
Age int16
User *User `orm:"reverse(one)"` // 设置一对一反向关系(可选)
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"` //设置一对多关系
Tags []*Tag `orm:"rel(m2m)"`
}
type Tag struct {
Id int
Name string
Posts []*Post `orm:"reverse(many)"`
}
func init() {
// 需要在init中注册定义的model
orm.RegisterModel(new(Userinfo),new(User), new(Profile), new(Tag))
}
```
>注意一点beego orm针对驼峰命名会自动帮你转化成下划线字段例如你定义了Struct名字为`User`,那么转化成底层实现的时候是`user_info`,字段命名也遵循该规则。
## 插入数据
下面的代码演示了如何插入一条记录可以看到我们操作的是struct对象而不是原生的sql语句最后通过调用Insert接口将数据保存到数据库。
```Go
o := orm.NewOrm()
var user User
user.Name = "zxxx"
user.Departname = "zxxx"
id, err := o.Insert(&user)
if err == nil {
fmt.Println(id)
}
```
我们看到插入之后`user.Uid`就是插入成功之后的自增ID。
同时插入多个对象:InsertMulti
类似sql语句
```Go
insert into table (name, age) values("slene", 28),("astaxie", 30),("unknown", 20)
```
第一个参数 bulk 为并列插入的数量第二个为对象的slice
返回值为成功插入的数量
```Go
users := []User{
{Name: "slene"},
{Name: "astaxie"},
{Name: "unknown"},
...
}
successNums, err := o.InsertMulti(100, users)
```
bulk 为 1 时,将会顺序插入 slice 中的数据
## 更新数据
继续上面的例子来演示更新操作现在user的主键已经有值了此时调用Insert接口beego orm内部会自动调用update以进行数据的更新而非插入操作。
```Go
o := orm.NewOrm()
user := User{Uid: 1}
if o.Read(&user) == nil {
user.Name = "MyName"
if num, err := o.Update(&user); err == nil {
fmt.Println(num)
}
}
```
Update 默认更新所有的字段,可以更新指定的字段:
```Go
// 只更新 Name
o.Update(&user, "Name")
// 指定多个字段
// o.Update(&user, "Field1", "Field2", ...)
```
//Where:用来设置条件支持多个参数第一个参数如果为整数相当于调用了Where("主键=?",值)。
## 查询数据
beego orm的查询接口比较灵活具体使用请看下面的例子
例子1根据主键获取数据
```Go
o := orm.NewOrm()
var user User
user := User{Id: 1}
err = o.Read(&user)
if err == orm.ErrNoRows {
fmt.Println("查询不到")
} else if err == orm.ErrMissPK {
fmt.Println("找不到主键")
} else {
fmt.Println(user.Id, user.Name)
}
```
例子2
```Go
o := orm.NewOrm()
var user User
qs := o.QueryTable(user) // 返回 QuerySeter
qs.Filter("id", 1) // WHERE id = 1
qs.Filter("profile__age", 18) // WHERE profile.age = 18
```
例子3WHERE IN查询条件
```Go
qs.Filter("profile__age__in", 18, 20)
// WHERE profile.age IN (18, 20)
```
例子4更加复杂的条件
```Go
qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000
```
可以通过如下接口获取多条数据,请看示例
例子1根据条件age>17获取20位置开始的10条数据的数据
```Go
var allusers []User
qs.Filter("profile__age__gt", 17)
// WHERE profile.age > 17
```
例子2limit默认从10开始获取10条数据
```Go
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20 注意跟SQL反过来的
```
## 删除数据
beedb提供了丰富的删除数据接口请看下面的例子
例子1删除单条数据
```Go
o := orm.NewOrm()
if num, err := o.Delete(&User{Id: 1}); err == nil {
fmt.Println(num)
}
```
Delete 操作会对反向关系进行操作,此例中 Post 拥有一个到 User 的外键。删除 User 的时候。如果 on_delete 设置为默认的级联操作,将删除对应的 Post
## 关联查询
有些应用却需要用到连接查询所以现在beego orm提供了一个简陋的实现方案
```Go
type Post struct {
Id int `orm:"auto"`
Title string `orm:"size(100)"`
User *User `orm:"rel(fk)"`
}
var posts []*Post
qs := o.QueryTable("post")
num, err := qs.Filter("User__Name", "slene").All(&posts)
```
上面代码中我们看到了一个struct关联查询
## Group By和Having
针对有些应用需要用到group by的功能beego orm也提供了一个简陋的实现
```Go
qs.OrderBy("id", "-profile__age")
// ORDER BY id ASC, profile.age DESC
qs.OrderBy("-profile__age", "profile")
// ORDER BY profile.age DESC, profile_id ASC
```
上面的代码中出现了两个新接口函数
GroupBy:用来指定进行groupby的字段
Having:用来指定having执行的时候的条件
##使用原生sql
简单示例:
```Go
o := NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
```
复杂原生sql使用:
```Go
func (m *User) Query(name string) []User {
var o orm.Ormer
var rs orm.RawSeter
o = orm.NewOrm()
rs = o.Raw("SELECT * FROM user "+
"WHERE name=? AND uid>10 "+
"ORDER BY uid DESC "+
"LIMIT 100", name)
var user []User
num, err := rs.QueryRows(&user)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(num)
return user
}
}
```
更多说明,请到[beego.me](https://beego.me)
## 进一步的发展
目前beego orm已经获得了很多来自国内外用户的反馈我目前也正在考虑支持更多数据库接下来会在更多方面进行改进
## links
* [目录](<preface.md>)
* 上一节: [使用PostgreSQL数据库](<05.4.md>)
* 下一节: [NOSQL数据库操作](<05.6.md>)