GoWeb——Beego ORM1.X详解

目录

1、ORM使用

1.1、表结构
CREATE TABLE IF NOT EXISTS `user` (
		`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
		`name` varchar(255) NOT NULL DEFAULT '' ,
		`profile_id` integer NOT NULL UNIQUE
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `post` (
		`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
		`title` varchar(255) NOT NULL DEFAULT '' ,
		`user_id` integer NOT NULL
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `profile` (
		`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
		`age` smallint NOT NULL DEFAULT 0 
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `tag` (
		`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
		`name` varchar(255) NOT NULL DEFAULT '' 
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `post_tags` (
		`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY,
		`post_id` integer NOT NULL,
		`tag_id` integer NOT NULL
) ENGINE=InnoDB;
1.2、models.go
package model

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id      int
	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(User), new(Post), new(Profile), new(Tag))
}
1.3、main.go
package main

import (
	"./model"
	"fmt"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

func init() {
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", "root:123456@/godb?charset=utf8")
}

func main() {
	o := orm.NewOrm()
	o.Using("default") //默认使用default,你可以指定为其他数据库

	profile := new(model.Profile)
	profile.Age = 30

	user := new(model.User)
	user.Profile = profile
	user.Name = "slene"

	fmt.Println(o.Insert(profile))
	fmt.Println(o.Insert(user))
}

2、数据库的设置

目前 ORM 支持三种数据库,将需要使用的 driver 加入 import 中即可

import (
    _ "github.com/go-sql-driver/mysql"
    _ "github.com/lib/pq"
    _ "github.com/mattn/go-sqlite3"
)
2.1、RegisterDriver

三种默认数据库类型

// For version 1.6
orm.DRMySQL
orm.DRSqlite
orm.DRPostgres

// < 1.6
orm.DR_MySQL
orm.DR_Sqlite
orm.DR_Postgres
// 参数1   driverName
// 参数2   数据库类型
// 这个用来设置 driverName 对应的数据库类型
// mysql / sqlite3 / postgres 这三种是默认已经注册过的,所以可以无需设置
orm.RegisterDriver("mysql", orm.DRMySQL)
2.2、RegisterDataBase

ORM 必须注册一个别名为 default 的数据库,作为默认使用,ORM 使用 golang 自己的连接池。

// 参数1        数据库的别名,用来在 ORM 中切换数据库使用
// 参数2        driverName
// 参数3        对应的链接字符串
orm.RegisterDataBase("default", "mysql", "root:123456@/godb?charset=utf8")


maxIdle := 30 // 参数4(可选)  设置最大空闲连接
maxConn := 30 // 参数5(可选)  设置最大数据库连接 (go >= 1.2)
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", maxIdle, maxConn)
2.3、SetMaxIdleConns

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

orm.SetMaxIdleConns("default", 30)
2.4、SetMaxOpenConns

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

orm.SetMaxOpenConns("default", 30)
2.5、时区设置

ORM 默认使用 time.Local 本地时区

  • 作用于 ORM 自动创建的时间
  • 从数据库中取回的时间转换成 ORM 本地时间

如果需要的话,你也可以进行更改

// 设置为 UTC 时间
orm.DefaultTimeLoc = time.UTC

ORM 在进行 RegisterDataBase 的同时,会获取数据库使用的时区,然后在 time.Time 类型存取时做相应转换,以匹配时间系统,从而保证时间不会出错。

注意:

  • 鉴于 Sqlite3 的设计,存取默认都为 UTC 时间
  • 使用 go-sql-driver 驱动时,请注意参数设置
    从某一版本开始,驱动默认使用 UTC 时间,而非本地时间,所以请指定时区参数或者全部以 UTC 时间存取
    例如:root:root@/orm_test?charset=utf8&loc=Asia%2FShanghai

3、注册模型

如果使用 orm.QuerySeter 进行高级查询的话,这个是必须的。反之,如果只使用 Raw 查询和 map struct,是无需这一步的。

3.1、RegisterModel

将你定义的 Model 进行注册,最佳设计是有单独的 models.go 文件,在他的 init 函数中进行注册。

package main
import "github.com/astaxie/beego/orm"
type User struct {
    Id   int
    Name string
}
func init(){
    orm.RegisterModel(new(User))
}

RegisterModel 也可以同时注册多个 model

orm.RegisterModel(new(User), new(Profile), new(Post))
3.2、RegisterModelWithPrefix

使用表名前缀

orm.RegisterModelWithPrefix("prefix_", new(User))

创建后的表名为 prefix_user

3.3、NewOrmWithDB

有时候需要自行管理连接池与数据库链接(比如:go 的链接池无法让两次查询使用同一个链接的)但又想使用 ORM 的查询功能

var driverName, aliasName string
// driverName 是驱动的名称
// aliasName 是当前 db 的自定义别名
var db *sql.DB
...
o := orm.NewOrmWithDB(driverName, aliasName, db)
3.4、GetDB

从已注册的数据库返回 *sql.DB 对象,默认返回别名为 default 的数据库。

db, err := orm.GetDB()
if err != nil {
    fmt.Println("get default DataBase")
}
db, err := orm.GetDB("alias")
if err != nil {
    fmt.Println("get alias DataBase")
}
3.5、ResetModelCache

重置已经注册的模型 struct,一般用于编写测试用例

orm.ResetModelCache()

4、ORM接口使用

var o Ormer
o = orm.NewOrm() // 创建一个 Ormer
// NewOrm 的同时会执行 orm.BootStrap (整个 app 只执行一次),用以验证模型之间的定义并缓存。

切换数据库,或者,进行事务处理,都会作用于这个 Ormer 对象,以及其进行的任何查询。所以:需要 切换数据库 和 事务处理 的话,不要使用全局保存的 Ormer 对象。

type Ormer interface {
	Read(interface{},string) error
	ReadOrCreate(interface{}, string,string) (bool, int64, error)
	Insert(interface{}) (int64, error)
	InsertMulti(int, interface{}) (int64, error)
	Update(interface{},string) (int64, error)
	Delete(interface{}) (int64, error)
	LoadRelated(interface{}, string,interface{}) (int64, error)
	QueryM2M(interface{}, string) QueryM2Mer
	QueryTable(interface{}) QuerySeter
	Using(string) error
	Begin() error
	Commit() error
	Rollback() error
	Raw(string,interface{}) RawSeter
	Driver() Driver
}
4.1、QueryTable

传入表名,或者 Model 对象,返回一个 QuerySeter

o := orm.NewOrm()
var qs QuerySeter
qs = o.QueryTable("user")
// 如果表没有定义过,会立刻 panic
4.2、Using

切换为其他数据库

orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8")
orm.RegisterDataBase("db2", "sqlite3", "data.db")
o1 := orm.NewOrm()
o1.Using("db1")
o2 := orm.NewOrm()
o2.Using("db2")
// 切换为其他数据库以后
// 这个 Ormer 对象的其下的 api 调用都将使用这个数据库

默认使用 default 数据库,无需调用 Using

4.3、Raw

使用 sql 语句直接进行操作。Raw 函数,返回一个 RawSeter 用以对设置的 sql 语句和参数进行操作

o := NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
4.4、Driver

返回当前ORM使用的db信息

type Driver interface {
    Name() string
    Type() DriverType
}
orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8")
orm.RegisterDataBase("db2", "sqlite3", "data.db")

o1 := orm.NewOrm()
o1.Using("db1")
dr := o1.Driver()
fmt.Println(dr.Name() == "db1") // true
fmt.Println(dr.Type() == orm.DRMySQL) // true

o2 := orm.NewOrm()
o2.Using("db2")
dr = o2.Driver()
fmt.Println(dr.Name() == "db2") // true
fmt.Println(dr.Type() == orm.DRSqlite) // true

5、调试模式打印查询语句

简单的设置 Debug 为 true 打印查询的语句,可能存在性能问题,不建议使用在产品模式

func main() {
    orm.Debug = true
...

默认使用 os.Stderr 输出日志信息,改变输出到你自己的 io.Writer

var w io.Writer
...
// 设置为你的 io.Writer
...
orm.DebugLog = orm.NewLog(w)

日志格式:

[ORM] - 时间 - [Queries/数据库名] - [执行操作/执行时间] - [SQL语句] - 使用标点 `,` 分隔的参数列表 - 打印遇到的错误
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [    db.Exec /     0.4ms] - [INSERT INTO `user` (`name`) VALUES (?)] - `slene`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [    db.Exec /     0.5ms] - [UPDATE `user` SET `name` = ? WHERE `id` = ?] - `astaxie`, `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [db.QueryRow /     0.4ms] - [SELECT `id`, `name` FROM `user` WHERE `id` = ?] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [    db.Exec /     0.4ms] - [INSERT INTO `post` (`user_id`,`title`,`content`) VALUES (?, ?, ?)] - `14`, `beego orm`, `powerful amazing`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [   db.Query /     0.4ms] - [SELECT T1.`name` `User__Name`, T0.`user_id` `User`, T1.`id` `User__Id` FROM `post` T0 INNER JOIN `user` T1 ON T1.`id` = T0.`user_id` WHERE T0.`id` = ? LIMIT 1000] - `68`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [    db.Exec /     0.4ms] - [DELETE FROM `user` WHERE `id` = ?] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [   db.Query /     0.3ms] - [SELECT T0.`id` FROM `post` T0 WHERE T0.`user_id` IN (?) ] - `14`
[ORM] - 2013-08-09 13:18:16 - [Queries/default] - [    db.Exec /     0.4ms] - [DELETE FROM `post` WHERE `id` IN (?)] - `68`

日志内容包括 所有的数据库操作,事务,Prepare,等。

6、对象的CRUD操作

如果已知主键的值,那么可以使用这些方法进行 CRUD 操作,对 object 操作的四个方法 Read / Insert / Update / Delete。

o := orm.NewOrm()
user := new(model.User)
user.Name = "slene"

fmt.Println(o.Insert(user))

user.Name = "Your"
fmt.Println(o.Update(user))
fmt.Println(o.Read(user))
fmt.Println(o.Delete(user))
6.1、Read
o := orm.NewOrm()
user := model.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)
}

Read 默认通过查询主键赋值,可以使用指定的字段进行查询:

user := User{Name: "slene"}
err = o.Read(&user, "Name")

对象的其他字段值将会是对应类型的默认值,复杂的单个对象查询参见 One方法。

6.2、ReadOrCreate

尝试从数据库读取,不存在的话就创建一个。默认必须传入一个参数作为条件字段,同时也支持多个参数多个条件字段。

o := orm.NewOrm()
user := model.User{Name: "slene"}

//3个返回参数依次为:是否新创建的、对象ID值、错误对象
if created, id, err := o.ReadOrCreate(&user, "Name"); err == nil {
	if created {
		fmt.Println("New Insert an object. Id:", id)
	} else {
		fmt.Println("Get an object. Id:", id)
	}
}
6.3、Insert

第一个返回值为自增键Id的值,创建后会自动对auto的field赋值

o := orm.NewOrm()
profile := new(model.Profile)
profile.Age = 18

user := new(model.User)
user.Profile = profile
user.Name = "tom"

pid, err1 := o.Insert(profile)
uid, err2 := o.Insert(user)

if err1 == nil && err2 == nil {
	fmt.Println("pid:", pid, "\tuid:", uid)
}
6.4、InsertMulti

同时插入多个对象,类似sql语句

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

第一个参数 bulk 为并列插入的数量,第二个为对象的slice,返回值为成功插入的数量

users := []User{
    {Name: "slene"},
    {Name: "astaxie"},
    {Name: "unknown"},
    ...
}
successNums, err := o.InsertMulti(100, users)
//bulk 为 1 时,将会顺序插入 slice 中的数据
6.5、Update

第一个返回值为影响的行数:

o := orm.NewOrm()
profile := model.Profile{Id: 1}
if o.Read(&profile) == nil {
	profile.Age = 99
	if num, err := o.Update(&profile); err == nil {
		fmt.Println(num)
	}
}

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

// 只更新 Name
o.Update(&user, "Name")
// 指定多个字段
// o.Update(&user, "Field1", "Field2", ...)
6.6、Delete

第一个返回值为影响的行数

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

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

注意:Changed in 1.0.3 删除以后不会删除 auto field 的值

7、高级查询

ORM 以 QuerySeter 来组织查询,每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象。
基本使用方法:

o := orm.NewOrm()
//获取QuerySeter对象,user为表名
qs := o.QueryTable("user")
//也可以直接使用对象名作为表名
user := new(model.User)
qs = o.QueryTable(user) //返回QuerySeter

fmt.Println(qs.(orm.QuerySeter))
7.1、expr

QuerySeter 中用于描述字段和 sql 操作符,使用简单的 expr 查询方法

字段组合的前后顺序依照表的关系,比如 User 表拥有 Profile 的外键,那么对 User 表查询对应的 Profile.Age 为条件,则使用 Profile__Age 注意,字段的分隔符号使用双下划线 __,除了描述字段, expr 的尾部可以增加操作符以执行对应的 sql 操作。比如 Profile__Age__gt 代表 Profile.Age > 18 的条件查询。

注释后面将描述对应的 sql 语句,仅仅是描述 expr 的类似结果,并不代表实际生成的语句。

qs.Filter("id", 1) // WHERE id = 1
qs.Filter("profile__age", 18) // WHERE profile.age = 18
qs.Filter("Profile__Age", 18) // 使用字段名和 Field 名都是允许的
qs.Filter("profile__age", 18) // WHERE profile.age = 18
qs.Filter("profile__age__gt", 18) // WHERE profile.age > 18
qs.Filter("profile__age__gte", 18) // WHERE profile.age >= 18
qs.Filter("profile__age__in", 18, 20) // WHERE profile.age IN (18, 20)
qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000) // WHERE profile.age IN (18, 20) AND NOT profile_id < 1000
7.2、Operators

当前支持的操作符号:

exact/iexact //等于
contains/icontains //包含
gt/gte //大于/大于等于
lt/lte //小于/小于等于
startswith/istartswith //以…起始
endswith/iendswith //以…结束
in
isnull

后面以 i 开头的表示:大小写不敏感

7.2.1、exact

Filter / Exclude / Condition expr 的默认值

qs.Filter("name", "slene") // WHERE name = 'slene'
qs.Filter("name__exact", "slene") // WHERE name = 'slene'
// 使用 = 匹配,大小写是否敏感取决于数据表使用的 collation
qs.Filter("profile_id", nil) // WHERE profile_id IS NULL
7.2.2、iexact
qs.Filter("name__iexact", "slene")
// WHERE name LIKE 'slene'
// 大小写不敏感,匹配任意 'Slene' 'sLENE'
7.2.3、contains
qs.Filter("name__contains", "slene")
// WHERE name LIKE BINARY '%slene%'
// 大小写敏感, 匹配包含 slene 的字符
7.2.4、icontains
qs.Filter("name__icontains", "slene")
// WHERE name LIKE '%slene%'
// 大小写不敏感, 匹配任意 'im Slene', 'im sLENE'
7.2.5、in
qs.Filter("profile__age__in", 17, 18, 19, 20)
// WHERE profile.age IN (17, 18, 19, 20)
ids:=[]int{17,18,19,20}
qs.Filter("profile__age__in", ids)
// WHERE profile.age IN (17, 18, 19, 20)
// 同上效果
7.2.6、gt/gte
qs.Filter("profile__age__gt", 17)
// WHERE profile.age > 17
qs.Filter("profile__age__gte", 18)
// WHERE profile.age >= 18
7.2.7、lt/lte
qs.Filter("profile__age__lt", 17)
// WHERE profile.age < 17
qs.Filter("profile__age__lte", 18)
// WHERE profile.age <= 18
7.2.8、startswith
qs.Filter("name__startswith", "slene")
// WHERE name LIKE BINARY 'slene%'
// 大小写敏感, 匹配以 'slene' 起始的字符串
7.2.9、istartswith
qs.Filter("name__iendswithi", "slene")
// WHERE name LIKE '%slene'
// 大小写不敏感, 匹配任意以 'slene', 'Slene' 结束的字符串
7.2.10、isnull
qs.Filter("profile__isnull", true)
qs.Filter("profile_id__isnull", true)
// WHERE profile_id IS NULL
qs.Filter("profile__isnull", false)
// WHERE profile_id IS NOT NULL
7.3、高级查询接口使用

QuerySeter 是高级查询使用的接口,我们来熟悉下他的接口方法

type QuerySeter interface {
	Filter(string,interface{}) QuerySeter
	Exclude(string,interface{}) QuerySeter
	SetCond(*Condition) QuerySeter
	Limit(int,int64) QuerySeter
	Offset(int64) QuerySeter
	GroupBy(string) QuerySeter
	OrderBy(string) QuerySeter
	Distinct() QuerySeter
	RelatedSel(interface{}) QuerySeter
	Count() (int64, error)
	Exist() bool
	Update(Params) (int64, error)
	Delete() (int64, error)
	PrepareInsert() (Inserter, error)
	All(interface{},string) (int64, error)
	One(interface{},string) error
	Values(*[]Params,string) (int64, error)
	ValuesList(*[]ParamsList,string) (int64, error)
	ValuesFlat(*ParamsList, string) (int64, error)
}
  • 每个返回 QuerySeter 的 api 调用时都会新建一个 QuerySeter,不影响之前创建的。
  • 高级查询使用 Filter 和 Exclude 来做常用的条件查询。囊括两种清晰的过滤规则:包含, 排除
7.3.1、Filter

用来过滤查询结果,起到包含条件的作用,多个 Filter 之间使用 AND 连接

qs.Filter("profile__isnull", true).Filter("name", "slene")
// WHERE profile_id IS NULL AND name = 'slene'
7.3.2、Exclude

用来过滤查询结果,起到 排除条件 的作用,使用 NOT 排除条件,多个 Exclude 之间使用 AND 连接

qs.Exclude("profile__isnull", true).Filter("name", "slene")
// WHERE NOT profile_id IS NULL AND name = 'slene'
7.3.3、SetCond

自定义条件表达式

cond := orm.NewCondition()
cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
qs := orm.QueryTable("user")
qs = qs.SetCond(cond1)
// WHERE ... AND ... AND NOT ... OR ...
cond2 := cond.AndCond(cond1).OrCond(cond.And("name", "slene"))
qs = qs.SetCond(cond2).Count()
// WHERE (... AND ... AND NOT ... OR ...) OR ( ... )
7.3.4、Limit

限制最大返回数据行数,第二个参数可以设置 Offset

var DefaultRowsLimit = 1000 // ORM 默认的 limit 值为 1000
// 默认情况下 select 查询的最大行数为 1000
// LIMIT 1000
qs.Limit(10)
// LIMIT 10
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20 注意跟 SQL 反过来的
qs.Limit(-1)
// no limit
qs.Limit(-1, 100)
// LIMIT 18446744073709551615 OFFSET 100
// 18446744073709551615 是 1<<64 - 1 用来指定无 limit 限制 但有 offset 偏移的情况
7.3.5、Offset

设置偏移行数

qs.Offset(20)
// LIMIT 1000 OFFSET 20
7.3.6、GroupBy
qs.GroupBy("id", "age")
// GROUP BY id,age
7.3.7、OrderBy

参数使用 expr,在 expr 前使用减号 - 表示 DESC 的排列

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
7.3.8、Distinct

对应 sql 的 distinct 语句, 返回不重复的值.

qs.Distinct()
// SELECT DISTINCT
7.3.9、RelatedSel

关系查询,参数使用 expr

var DefaultRelsDepth = 5 // 默认情况下直接调用 RelatedSel 将进行最大 5 层的关系查询
qs := o.QueryTable("post")
qs.RelatedSel()
// INNER JOIN user ... LEFT OUTER JOIN profile ...
qs.RelatedSel("user")
// INNER JOIN user ...
// 设置 expr 只对设置的字段进行关系查询
// 对设置 null 属性的 Field 将使用 LEFT OUTER JOIN
7.3.10、Count

依据当前的查询条件,返回结果行数

cnt, err := o.QueryTable("user").Count() // SELECT COUNT(*) FROM USER
fmt.Printf("Count Num: %s, %s", cnt, err)
7.3.11、Update

依据当前查询条件,进行批量更新操作

num, err := o.QueryTable("user").Filter("name", "slene").Update(orm.Params{
    "name": "astaxie",
})
fmt.Printf("Affected Num: %s, %s", num, err)
// SET name = "astaixe" WHERE name = "slene"

原子操作增加字段值:

// 假设 user struct 里有一个 nums int 字段
num, err := o.QueryTable("user").Update(orm.Params{
    "nums": orm.ColValue(orm.ColAdd, 100),
})
// SET nums = nums + 100

orm.ColValue支持以下操作

ColAdd      // 加
ColMinus    // 减
ColMultiply // 乘
ColExcept   // 除
7.3.12、Delete

依据当前查询条件,进行批量删除操作

num, err := o.QueryTable("user").Filter("name", "slene").Delete()
fmt.Printf("Affected Num: %s, %s", num, err)
// DELETE FROM user WHERE name = "slene"
7.3.13、PrepareInsert

用于一次 prepare 多次 insert 插入,以提高批量插入的速度。

o := orm.NewOrm()
qs := o.QueryTable("profile")
profiles := []*model.Profile{&model.Profile{Age: 1}, &model.Profile{Age: 2}}
i, _ := qs.PrepareInsert()
for _, p := range profiles {
	id, err := i.Insert(p)
	if err == nil {
		fmt.Println(id)
	}
}
// PREPARE INSERT INTO profile (`age`, ...) VALUES (?, ...)
// EXECUTE INSERT INTO profile (`age`, ...) VALUES (1, ...)
// EXECUTE ...
// ...
i.Close() // 别忘记关闭 statement
7.3.14、All

返回对应的结果集对象,All 的参数支持 []Type 和 []*Type 两种形式的 slice

var users []*User
num, err := o.QueryTable("user").Filter("name", "slene").All(&users)
fmt.Printf("Returned Rows Num: %s, %s", num, err)

All / Values / ValuesList / ValuesFlat 受到 Limit 的限制,默认最大行数为 1000

可以指定返回的字段:

type Post struct {
    Id      int
    Title   string
    Content string
    Status  int
}
// 只返回 Id 和 Title
var posts []Post
o.QueryTable("post").Filter("Status", 1).All(&posts, "Id", "Title")

对象的其他字段值将会是对应类型的默认值

7.3.15、One

尝试返回单条记录

var user User
err := o.QueryTable("user").Filter("name", "slene").One(&user)
if err == orm.ErrMultiRows {
    // 多条的时候报错
    fmt.Printf("Returned Multi Rows Not One")
}
if err == orm.ErrNoRows {
    // 没有找到记录
    fmt.Printf("Not row found")
}

可以指定返回的字段:

// 只返回 Id 和 Title
var post Post
o.QueryTable("post").Filter("Content__istartswith", "prefix string").One(&post, "Id", "Title")

对象的其他字段值将会是对应类型的默认值

7.3.16、Values

返回结果集的 key => value 值

key 为 Model 里的 Field name,value 的值 以 string 保存

var maps []orm.Params
num, err := o.QueryTable("user").Values(&maps)
if err == nil {
    fmt.Printf("Result Nums: %d\n", num)
    for _, m := range maps {
        fmt.Println(m["Id"], m["Name"])
    }
}

返回指定的 Field 数据

TODO: 暂不支持级联查询 RelatedSel 直接返回 Values,但可以直接指定 expr 级联返回需要的数据

var maps []orm.Params
num, err := o.QueryTable("user").Values(&maps, "id", "name", "profile", "profile__age")
if err == nil {
    fmt.Printf("Result Nums: %d\n", num)
    for _, m := range maps {
        fmt.Println(m["Id"], m["Name"], m["Profile"], m["Profile__Age"])
        // map 中的数据都是展开的,没有复杂的嵌套
    }
}
7.3.17、ValuesList

顾名思义,返回的结果集以slice存储,结果的排列与 Model 中定义的 Field 顺序一致,返回的每个元素值以 string 保存

var lists []orm.ParamsList
num, err := o.QueryTable("user").ValuesList(&lists)
if err == nil {
    fmt.Printf("Result Nums: %d\n", num)
    for _, row := range lists {
        fmt.Println(row)
    }
}

当然也可以指定 expr 返回指定的 Field

var lists []orm.ParamsList
num, err := o.QueryTable("user").ValuesList(&lists, "name", "profile__age")
if err == nil {
    fmt.Printf("Result Nums: %d\n", num)
    for _, row := range lists {
        fmt.Printf("Name: %s, Age: %s\m", row[0], row[1])
    }
}
7.3.18、ValuesFlat

只返回特定的 Field 值,将结果集展开到单个 slice 里

var list orm.ParamsList
num, err := o.QueryTable("user").ValuesFlat(&list, "name")
if err == nil {
    fmt.Printf("Result Nums: %d\n", num)
    fmt.Printf("All User Names: %s", strings.Join(list, ", "))
}
7.4、关系查询
7.4.1、User 和 Profile 是 OneToOne 的关系

已经取得了 User 对象,查询 Profile:

user := &User{Id: 1}
o.Read(user)
if user.Profile != nil {
    o.Read(user.Profile)
}

直接关联查询:

user := &User{}
o.QueryTable("user").Filter("Id", 1).RelatedSel().One(user)
// 自动查询到 Profile
fmt.Println(user.Profile)
// 因为在 Profile 里定义了反向关系的 User,所以 Profile 里的 User 也是自动赋值过的,可以直接取用。
fmt.Println(user.Profile.User)
// [SELECT T0.`id`, T0.`name`, T0.`profile_id`, T1.`id`, T1.`age` FROM `user` T0 INNER JOIN `profile` T1 ON T1.`id` = T0.`profile_id` WHERE T0.`id` = ? LIMIT 1000] - `1`

通过 User 反向查询 Profile:

var profile Profile
err := o.QueryTable("profile").Filter("User__Id", 1).One(&profile)
if err == nil {
    fmt.Println(profile)
}
7.4.2、Post 和 User 是 ManyToOne 关系,也就是 ForeignKey 为 User
type Post struct {
    Id    int
    Title string
    User  *User  `orm:"rel(fk)"`
    Tags  []*Tag `orm:"rel(m2m)"`
}
var posts []*Post
num, err := o.QueryTable("post").Filter("User", 1).RelatedSel().All(&posts)
if err == nil {
    fmt.Printf("%d posts read\n", num)
    for _, post := range posts {
        fmt.Printf("Id: %d, UserName: %d, Title: %s\n", post.Id, post.User.UserName, post.Title)
    }
}
// [SELECT T0.`id`, T0.`title`, T0.`user_id`, T1.`id`, T1.`name`, T1.`profile_id`, T2.`id`, T2.`age` FROM `post` T0 INNER JOIN `user` T1 ON T1.`id` = T0.`user_id` INNER JOIN `profile` T2 ON T2.`id` = T1.`profile_id` WHERE T0.`user_id` = ? LIMIT 1000] - `1`

根据 Post.Title 查询对应的 User:

RegisterModel 时,ORM 也会自动建立 User 中 Post 的反向关系,所以可以直接进行查询

var user User
err := o.QueryTable("user").Filter("Post__Title", "The Title").Limit(1).One(&user)
if err == nil {
    fmt.Printf(user)
}
7.4.3、Post 和 Tag 是 ManyToMany 关系

设置 rel(m2m) 以后,ORM 会自动创建中间表

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)"`
}

一条 Post 纪录可能对应不同的 Tag 纪录,一条 Tag 纪录可能对应不同的 Post 纪录,所以 Post 和 Tag 属于多对多关系,通过 tag name 查询哪些 post 使用了这个 tag

通过 post title 查询这个 post 有哪些 tag

var tags []*Tag
num, err := dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduce Beego ORM").All(&tags)

8、载入关系字段

LoadRelated 用于载入模型的关系字段,包括所有的 rel/reverse - one/many 关系

ManyToMany 关系字段载入

// 载入相应的 Tags
post := Post{Id: 1}
err := o.Read(&post)
num, err := o.LoadRelated(&post, "Tags")
// 载入相应的 Posts
tag := Tag{Id: 1}
err := o.Read(&tag)
num, err := o.LoadRelated(&tag, "Posts")

User 是 Post 的 ForeignKey,对应的 ReverseMany 关系字段载入

type User struct {
    Id    int
    Name  string
    Posts []*Post `orm:"reverse(many)"`
}
user := User{Id: 1}
err := dORM.Read(&user)
num, err := dORM.LoadRelated(&user, "Posts")
for _, post := range user.Posts {
    //...
}

9、多对多关系操作

type QueryM2Mer interface {
	Add(interface{}) (int64, error)
	Remove(interface{}) (int64, error)
	Exist(interface{}) bool
	Clear() (int64, error)
	Count() (int64, error)
}

创建一个 QueryM2Mer 对象

o := orm.NewOrm()
post := Post{Id: 1}
m2m := o.QueryM2M(&post, "Tags")
// 第一个参数的对象,主键必须有值
// 第二个参数为对象需要操作的 M2M 字段
// QueryM2Mer 的 api 将作用于 Id 为 1 的 Post
9.1、QueryM2Mer Add
tag := &Tag{Name: "golang"}
o.Insert(tag)
num, err := m2m.Add(tag)
if err == nil {
    fmt.Println("Added nums: ", num)
}

Add 支持多种类型 Tag Tag []Tag []Tag []interface{}

var tags []*Tag
...
// 读取 tags 以后
...
num, err := m2m.Add(tags)
if err == nil {
    fmt.Println("Added nums: ", num)
}
// 也可以多个作为参数传入
// m2m.Add(tag1, tag2, tag3)
9.2、QueryM2Mer Remove

从M2M关系中删除 tag,Remove 支持多种类型 Tag Tag []Tag []Tag []interface{}

var tags []*Tag
...
// 读取 tags 以后
...
num, err := m2m.Remove(tags)
if err == nil {
    fmt.Println("Removed nums: ", num)
}
// 也可以多个作为参数传入
// m2m.Remove(tag1, tag2, tag3)
9.3、QueryM2Mer Exist

判断 Tag 是否存在于 M2M 关系中

if m2m.Exist(&Tag{Id: 2}) {
    fmt.Println("Tag Exist")
}
9.4、QueryM2Mer Clear

清除所有 M2M 关系

nums, err := m2m.Clear()
if err == nil {
    fmt.Println("Removed Tag Nums: ", nums)
}
9.5、QueryM2Mer Count

计算Tag的数量

nums, err := m2m.Count()
if err == nil {
    fmt.Println("Total Nums: ", nums)
}

10、原生SQL语句进行查询

  • 使用 Raw SQL 查询,无需使用 ORM 表定义
  • 多数据库,都可直接使用占位符号 ?,自动转换
  • 查询时的参数,支持使用 Model Struct 和 Slice, Array
ids := []int{1, 2, 3}
p.Raw("SELECT name FROM user WHERE id IN (?, ?, ?)", ids)

创建一个 RawSeter

o := orm.NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
type RawSeter interface {
	Exec() (sql.Result, error)
	QueryRow(interface{}) error
	QueryRows(interface{}) (int64, error)
	SetArgs(interface{}) RawSeter
	Values(*[]Params,string) (int64, error)
	ValuesList(*[]ParamsList,string) (int64, error)
	ValuesFlat(*ParamsList, string) (int64, error)
	RowsToMap(*Params, string, string) (int64, error)
	RowsToStruct(interface{}, string, string) (int64, error)
	Prepare() (RawPreparer, error)
}
10.1、Exec

执行 sql 语句,返回 sql.Result 对象

res, err := o.Raw("UPDATE user SET name = ?", "your").Exec()
if err == nil {
    num, _ := res.RowsAffected()
    fmt.Println("mysql row affected nums: ", num)
}
10.2、QueryRow

QueryRow 和 QueryRows 提供高级 sql mapper 功能

//支持 struct
o := orm.NewOrm()
type User struct {
	Id   int
	Name string
}
var user User
err := o.Raw("SELECT id, name FROM user WHERE id = ?", 1).QueryRow(&user)
if err != nil {
	fmt.Println(err)
}
fmt.Println(user)
10.3、QueryRows

QueryRows 支持的对象还有 map 规则是和 QueryRow 一样的,但都是 slice

o := orm.NewOrm()
type User struct {
	Id   int
	Name string
}
var users []User
num, err := o.Raw("SELECT id, name FROM user WHERE name = ?", "tom").QueryRows(&users)
if err != nil {
	fmt.Println(err)
}
fmt.Println("user nums: ", num)
fmt.Println(users)
10.4、SetArgs

改变 Raw(sql, args…) 中的 args 参数,返回一个新的 RawSeter,用于单条 sql 语句,重复利用,替换参数然后执行。

res, err := r.SetArgs("arg1", "arg2").Exec()
res, err := r.SetArgs("arg1", "arg2").Exec()
...
10.5、Values / ValuesList / ValuesFlat

Raw SQL 查询获得的结果集 Value 为 string 类型,NULL 字段的值为空

from beego 1.1.0
Values, ValuesList, ValuesFlat 的参数,可以指定返回哪些 Columns 的数据
通常情况下,是无需指定的,因为 sql 语句中你可以自行设置 SELECT 的字段
10.5.1、Values

返回结果集的 key => value 值

var maps []orm.Params
num, err = o.Raw("SELECT user_name FROM user WHERE status = ?", 1).Values(&maps)
if err == nil && num > 0 {
    fmt.Println(maps[0]["user_name"]) // slene
}
10.5.2、ValuesList

返回结果集 slice

var lists []orm.ParamsList
num, err = o.Raw("SELECT user_name FROM user WHERE status = ?", 1).ValuesList(&lists)
if err == nil && num > 0 {
    fmt.Println(lists[0][0]) // slene
}
10.5.3、ValuesFlat

返回单一字段的平铺 slice 数据

var list orm.ParamsList
num, err = o.Raw("SELECT id FROM user WHERE id < ?", 10).ValuesFlat(&list)
if err == nil && num > 0 {
    fmt.Println(list) // []{"1","2","3",...}
}
10.6、RowsToMap

SQL 查询结果是这样

namevalue
total100
found200

查询结果匹配到 map 里

res := make(orm.Params)
nums, err := o.Raw("SELECT name, value FROM options_table").RowsToMap(&res, "name", "value")
// res is a map[string]interface{}{
//    "total": 100,
//    "found": 200,
// }
10.7、RowsToStruct

SQL 查询结果是这样

namevalue
total100
found200

查询结果匹配到 struct 里

type Options struct {
    Total int
    Found int
}
res := new(Options)
nums, err := o.Raw("SELECT name, value FROM options_table").RowsToStruct(res, "name", "value")
fmt.Println(res.Total) // 100
fmt.Println(res.Found) // 200

//匹配支持的名称转换为 snake -> camel, eg: SELECT user_name … 需要你的 struct 中定义有 UserName
10.8、Prepare

用于一次 prepare 多次 exec,以提高批量执行的速度。

p, err := o.Raw("UPDATE user SET name = ? WHERE name = ?").Prepare()
res, err := p.Exec("testing", "slene")
res, err  = p.Exec("testing", "astaxie")
...
...
p.Close() // 别忘记关闭 statement

11、构造查询

QueryBuilder 提供了一个简便,流畅的 SQL 查询构造器。在不影响代码可读性的前提下用来快速的建立 SQL 语句。

QueryBuilder 在功能上与 ORM 重合, 但是各有利弊。ORM 更适用于简单的 CRUD 操作,而 QueryBuilder 则更适用于复杂的查询,例如查询中包含子查询和多重联结。

使用方法:

// User 包装了下面的查询结果
type User struct {
    Name string
    Age  int
}
var users []User
// 获取 QueryBuilder 对象. 需要指定数据库驱动参数。
// 第二个返回值是错误对象,在这里略过
qb, _ := orm.NewQueryBuilder("mysql")
// 构建查询对象
qb.Select("user.name",
    "profile.age").
    From("user").
    InnerJoin("profile").On("user.id_user = profile.fk_user").
    Where("age > ?").
    OrderBy("name").Desc().
    Limit(10).Offset(0)
// 导出 SQL 语句
sql := qb.String()
// 执行 SQL 语句
o := orm.NewOrm()
o.Raw(sql, 20).QueryRows(&users)

完整 API 接口:

type QueryBuilder interface {
    Select(fields ...string) QueryBuilder
    From(tables ...string) QueryBuilder
    InnerJoin(table string) QueryBuilder
    LeftJoin(table string) QueryBuilder
    RightJoin(table string) QueryBuilder
    On(cond string) QueryBuilder
    Where(cond string) QueryBuilder
    And(cond string) QueryBuilder
    Or(cond string) QueryBuilder
    In(vals ...string) QueryBuilder
    OrderBy(fields ...string) QueryBuilder
    Asc() QueryBuilder
    Desc() QueryBuilder
    Limit(limit int) QueryBuilder
    Offset(offset int) QueryBuilder
    GroupBy(fields ...string) QueryBuilder
    Having(cond string) QueryBuilder
    Subquery(sub string, alias string) string
    String() string
}

12、事务处理

ORM 可以简单的进行事务操作

o := NewOrm()
err := o.Begin()
// 事务处理过程
...
...
// 此过程中的所有使用 o Ormer 对象的查询都在事务处理范围内
if SomeError {
    err = o.Rollback()
} else {
    err = o.Commit()
}

13、模型定义

复杂的模型定义不是必须的,此功能用作数据库数据转换和自动建表

默认的表名规则,使用驼峰转蛇形:

AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user

除了开头的大写字母以外,遇到大写会增加 _,原名称中的下划线保留。

13.1、自定义表名
type User struct {
    Id int
    Name string
}
func (u *User) TableName() string {
    return "auth_user"
}

如果前缀设置为 prefix_ 那么表名为:prefix_auth_user

13.2、自定义索引

为单个或多个字段增加索引

type User struct {
    Id    int
    Name  string
    Email string
}
// 多字段索引
func (u *User) TableIndex() [][]string {
    return [][]string{
        []string{"Id", "Name"},
    }
}
// 多字段唯一键
func (u *User) TableUnique() [][]string {
    return [][]string{
        []string{"Name", "Email"},
    }
}
13.3、自定义引擎

仅支持 MySQL

默认使用的引擎,为当前数据库的默认引擎,这个是由你的 mysql 配置参数决定的。

你可以在模型里设置 TableEngine 函数,指定使用的引擎

type User struct {
    Id    int
    Name  string
    Email string
}
// 设置引擎为 INNODB
func (u *User) TableEngine() string {
    return "INNODB"
}
13.4、设置参数
orm:"null;rel(fk)"

多个设置间使用 ; 分隔,设置的值如果是多个,使用 , 分隔。

13.4.1、忽略字段

设置 - 即可忽略 struct 中的字段

type User struct {
...
    AnyField string `orm:"-"`
...
}
13.4.2、auto

当 Field 类型为 int, int32, int64, uint, uint32, uint64 时,可以设置字段为自增健

type Student struct {
    StuId int    `orm:"pk;auto"`  //设置主键自增长 
}

当模型定义里没有主键时,符合上述类型且名称为 Id 的 Field 将被视为自增健。

鉴于 go 目前的设计,即使使用了 uint64,但你也不能存储到他的最大值。依然会作为 int64 处理。

13.4.3、pk

设置为主键,适用于自定义其他类型为主键

type Student struct {
    StuId int    `orm:"pk;auto"`  //设置主键自增长 
}
13.4.4、null

数据库表默认为 NOT NULL,设置 null 代表 ALLOW NULL

Name string `orm:"null"`
13.4.5、index

为单个字段增加索引

name string `orm:"index"`
13.4.6、unique

为单个字段增加 unique 键

Name string `orm:"unique"`
13.4.7、column

为字段设置 db 字段的名称

Name string `orm:"column(user_name)"`
13.4.8、size

string 类型字段默认为 varchar(255)
设置 size 以后,db type 将使用 varchar(size)

Title string `orm:"size(60)"`
13.4.9、digits / decimals

设置 float32, float64 类型的浮点精度

Money float64 `orm:"digits(12);decimals(4)"`

总长度 12 小数点后 4 位 eg: 99999999.9999

13.4.10、auto_now / auto_now_add
Created time.Time `orm:"auto_now_add;type(datetime)"`
Updated time.Time `orm:"auto_now;type(datetime)"`

auto_now 每次 model 保存时都会对时间自动更新
auto_now_add 第一次保存时才设置时间
对于批量的 update 此设置是不生效的

13.4.11、type

设置为 date 时,time.Time 字段的对应 db 类型使用 date

Created time.Time `orm:"auto_now_add;type(date)"`

设置为 datetime 时,time.Time 字段的对应 db 类型使用 datetime

Created time.Time `orm:"auto_now_add;type(datetime)"`
13.4.12、default

为字段设置默认值,类型必须符合(目前仅用于级联删除时的默认值)

type User struct {
    ...
    Status int `orm:"default(1)"`
    ...
}

14、表关系设置

14.1、rel / reverse
14.1.1、RelOneToOne
type User struct {
    ...
    Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
    ...
}

对应的反向关系 RelReverseOne:

type Profile struct {
    ...
    User *User `orm:"reverse(one)"`
    ...
}
14.1.2、RelForeignKey
type Post struct {
    ...
    User *User `orm:"rel(fk)"` //一对多 RelForeignKey relation
    ...
}

对应的反向关系 RelReverseMany:

type User struct {
    ...
    Posts []*Post `orm:"reverse(many)"` //反向一对多 fk 的反向关系
    ...
}
14.1.3、RelManyToMany
type Post struct {
    ...
    Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation
    ...
}

对应的反向关系 RelReverseMany:

type Tag struct {
    ...
    Posts []*Post `orm:"reverse(many)"`
    ...
}
14.2、rel_table / rel_through

此设置针对 orm:“rel(m2m)” 的关系字段

rel_table       设置自动生成的 m2m 关系表的名称
rel_through     如果要在 m2m 关系中使用自定义的 m2m 关系表
                通过这个设置其名称,格式为 pkg.path.ModelName
                eg: app.models.PostTagRel
                PostTagRel 表需要有到 Post 和 Tag 的关系

当设置 rel_table 时会忽略 rel_through

设置方法:

orm:"rel(m2m);rel_table(the_table_name)"
orm:"rel(m2m);rel_through(pkg.path.ModelName)"
14.3、on_delete

设置对应的 rel 关系删除时,如何处理关系字段。

cascade        级联删除(默认值)
set_null       设置为 NULL,需要设置 null = true
set_default    设置为默认值,需要设置 default 值
do_nothing     什么也不做,忽略
type User struct {
    ...
    Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
    ...
}
type Profile struct {
    ...
    User *User `orm:"reverse(one)"`
    ...
}
// 删除 Profile 时将设置 User.Profile 的数据库字段为 NULL

关于 on_delete 的相关例子

type User struct {
    Id int
    Name string
}
type Post struct {
    Id int
    Title string
    User *User `orm:"rel(fk)"`
}

假设 Post -> User 是 ManyToOne 的关系,也就是外键。

o.Filter("Id", 1).Delete()

这个时候即会删除 Id 为 1 的 User 也会删除其发布的 Post
不想删除的话,需要设置 set_null

type Post struct {
    Id int
    Title string
    User *User `orm:"rel(fk);null;on_delete(set_null)"`
}

那这个时候,删除 User 只会把对应的 Post.user_id 设置为 NULL

当然有时候为了高性能的需要,多存点数据无所谓啊,造成批量删除才是问题。

type Post struct {
    Id int
    Title string
    User *User `orm:"rel(fk);null;on_delete(do_nothing)"`
}

那么只要删除的时候,不操作 Post 就可以了。

14.4、模型字段与数据库类型的对应

在此列出 ORM 推荐的对应数据库类型,自动建表功能也会以此为标准。

默认所有的字段都是 NOT NULL

14.4.1、MySQL
gomysql
int, int32 - 设置 auto 或者名称为 Id 时integer AUTO_INCREMENT
int64 - 设置 auto 或者名称为 Id 时bigint AUTO_INCREMENT
uint, uint32 - 设置 auto 或者名称为 Id 时integer unsigned AUTO_INCREMENT
uint64 - 设置 auto 或者名称为 Id 时bigint unsigned AUTO_INCREMENT
boolbool
string - 默认为 size 255varchar(size)
string - 设置 type(text) 时longtext
time.Time - 设置 type 为 date 时date
time.Timedatetime
bytetinyint unsigned
runeinteger
intinteger
int8tinyint
int16smallint
int32integer
int64bigint
uintinteger unsigned
uint8tinyint unsigned
uint16smallint unsigned
uint32integer unsigned
uint64bigint unsigned
float32double precision
float64double precision
float64 - 设置 digits, decimals 时numeric(digits, decimals)
14.4.2、Sqlite3
gosqlite3
int, int32, int64, uint, uint32, uint64 - 设置 auto 或者名称为 Id 时integer AUTOINCREMENT
boolbool
string - 默认为 size 255varchar(size)
string - 设置 type(text) 时text
time.Time - 设置 type 为 date 时date
time.Timedatetime
bytetinyint unsigned
runeinteger
intinteger
int8tinyint
int16smallint
int32integer
int64bigint
uintinteger unsigned
uint8tinyint unsigned
uint16smallint unsigned
uint32integer unsigned
uint64bigint unsigned
float32real
float64real
float64 - 设置 digits, decimals 时decimal
14.4.3、PostgreSQL
gopostgres
int, int32, int64, uint, uint32, uint64 - 设置 auto 或者名称为 Id 时serial
boolbool
string - 若没有指定 size 默认为 textvarchar(size)
string - 设置 type(text) 时text
string - 设置 type(json) 时json
string - 设置 type(jsonb) 时jsonb
time.Time - 设置 type 为 date 时date
time.Timetimestamp with time zone
bytesmallint CHECK(“column” >= 0 AND “column” <= 255)
runeinteger
intinteger
int8smallint CHECK(“column” >= -127 AND “column” <= 128)
int16smallint
int32integer
int64bigint
uintbigint CHECK(“column” >= 0)
uint8smallint CHECK(“column” >= 0 AND “column” <= 255)
uint16integer CHECK(“column” >= 0)
uint32bigint CHECK(“column” >= 0)
uint64bigint CHECK(“column” >= 0)
float32double precision
float64double precision
float64 - 设置 digits, decimals 时numeric(digits, decimals)

15、关系型字段

其字段类型取决于对应的主键。

  • RelForeignKey
  • RelOneToOne
  • RelManyToMany
  • RelReverseOne
  • RelReverseMany

16、命令模式

注册模型与数据库以后,调用 RunCommand 执行 orm 命令。

func main() {
    // orm.RegisterModel...
    // orm.RegisterDataBase...
    ...
    orm.RunCommand()
}
go build main.go
./main orm
# 直接执行可以显示帮助
# 如果你的程序可以支持的话,直接运行 go run main.go orm 也是一样的效果
16.1、自动建表
./main orm syncdb -h
Usage of orm command: syncdb:
  -db="default": DataBase alias name
  -force=false: drop tables before create
  -v=false: verbose info

使用 -force=1 可以 drop table 后再建表
使用 -v 可以查看执行的 sql 语句

在程序中直接调用自动建表:

// 数据库别名
name := "default"
// drop table 后再建表
force := true
// 打印执行过程
verbose := true
// 遇到错误立即返回
err := orm.RunSyncdb(name, force, verbose)
if err != nil {
    fmt.Println(err)
}

自动建表功能在非 force 模式下,是会自动创建新增加的字段的。也会创建新增加的索引。

对于改动过的旧字段,旧索引,需要用户自行进行处理。

16.2、打印建表SQL
./main orm sqlall -h
Usage of orm command: syncdb:
  -db="default": DataBase alias name

默认使用别名为 default 的数据库。

17、ORM Test

17.1、MySQL
mysql -u root -e 'create database orm_test;'
export ORM_DRIVER=mysql
export ORM_SOURCE="root:@/orm_test?charset=utf8"
go test -v github.com/astaxie/beego/orm
17.2、Sqlite3
touch /path/to/orm_test.db
export ORM_DRIVER=sqlite3
export ORM_SOURCE=/path/to/orm_test.db
go test -v github.com/astaxie/beego/orm
17.3、PostgreSQL
psql -c 'create database orm_test;' -U postgres
export ORM_DRIVER=postgres
export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
go test -v github.com/astaxie/beego/orm

18、自定义字段 Custom Fields

TypeBooleanField = 1 << iota
// string
TypeCharField
// string
TypeTextField
// time.Time
TypeDateField
// time.Time
TypeDateTimeField
// int16
TypeSmallIntegerField
// int32
TypeIntegerField
// int64
TypeBigIntegerField
// uint16
TypePositiveSmallIntegerField
// uint32
TypePositiveIntegerField
// uint64
TypePositiveBigIntegerField
// float64
TypeFloatField
// float64
TypeDecimalField
RelForeignKey
RelOneToOne
RelManyToMany
RelReverseOne
RelReverseMany
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值