beego的orm模型定義學習與實際測試

beego中的orm學習

標籤:beego orm

因爲我沒怎麼用過orm,一般就直接用着sql驅動直接上了。數據庫上的也有一段時間了,對於各種關係忘得也是差不多了。

這裏主要是官方寫的不是特別好,比如官方中文文檔在這裏給出的models.go的例子,讓人看的很迷茫。

package main

import (
    "github.com/astaxie/beego/orm"
)

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

這……我知道那些標籤能夠用反射獲得,可是orm後面的那些什麼一對一、一對多關係都是什麼東西?下面將記錄一些我在orm學習的一些坑點,以及數據庫知識與orm知識的回顧與結合。

使用orm進行原始的SQL操作

主要包括三個:直接用Raw函數,使用Exec執行SQL指令與事務提交。一般而言orm就不要直接用SQL了,不然和直接使用SQL驅動有什麼區別。

設置參數

終於找到了,在官方文檔的模型定義這裏。模型定義功能主要用於數據庫數據轉換和自動建表,對於我這種比較喜歡把所有相關的語句放在一個地方,同時又不想寫SQL語句的人而言,這個功能還是值得深入研究的。(畢竟能夠在本語言內解決的東西最好還是不要再加技術棧)

設置參數:orm:"null;rel(fk)",多個設置之間使用;進行分隔。設置的值如果是多個,使用,進行分隔。

忽略字段

設置-可忽略struct中的字段,比如

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

auto

當Field類型爲int等類型的時候,可以設置字段爲自增鍵(extra:auto_increment)。當模型定義中沒有主鍵時,符合上述類型且名稱爲Id的Field將被視爲自增鍵。

pk

primary key,設置爲逐漸。適用於定義其他名稱與類型爲主鍵。

null

數據庫表默認爲NOT NULL,設置null代表允許,即ALLOW NULL,比如Name string orm:"null"

index

爲單個字段增加索引(模型定義的開頭有提到複合索引)

unique

爲單個字段增加unique鍵

column

爲字段設置db字段的名稱(不設置將會自動轉換,轉換規則也是在模型定義頁面的開頭)。

    Name string `orm:"column(user_name)"`

size

string類型默認爲varchar(255),設置size之後會變爲varchar(size)

    Title string `orm:"size(60)"`

digits/decimals

設置float32,float64類型的浮點精度

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

即總長度12,小數點後4位

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此設置是不生效的。

type

設置爲date時,time.Time字段的對應db類型使用date。設置爲datetime時,time.Time字段的對應db類型使用datetime

default

爲字段設置默認值,類型必須符合(目前僅用於級聯刪除時的默認值)

Comment

    type User struct {
        ...
        Status int `orm:"default(1)" description:(這是狀態字段)`
        ...
    }

爲字段添加註釋,註釋中禁止包含引號

表關係設置

rel/reverse

關係參考了Django ORM 一對一、一對多、多對多詳解,例子使用的是官方中文教程的例子。

一對一關係

子表從母表中選出一條數據一一對應,母表中選出來一條就少一條,子表中不可以再選擇母表中已經選擇的數據。

從數學的角度上來說,就是將子表作爲定義域,母表作爲值域的單射(不確定是否是滿射,好像可以不是的樣子)

RelOneToOne:

type User struct {
    ...
    Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
    ...
}

對應的反向關係RelReverseOne

type Profile struct {
    ...
    User *User `orm:"reverse(one)"`
    ...
}

實驗打出來的表格信息:

create table `user`
    -- --------------------------------------------------
    --  Table Structure for `main.User`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `user` (
        `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
        `profile_id` integer UNIQUE
    ) ENGINE=InnoDB;

create table `profile`
    -- --------------------------------------------------
    --  Table Structure for `main.Profile`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `profile` (
        `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY
    ) ENGINE=InnoDB;

可以看到實現上是作爲Unique的對象,並沒有實現類似外鍵之類的東西。實際在添加的時候也是並沒有說我加了User,然後Profile就不用再添加了。實際上還是要一起添加的,不會去檢查存在性與否,在數據庫上的實現真的就是多了一個一對一的id作爲Unique的值而已,並沒有什麼其他的區別。

感覺還是可以酌情添加吧,場景上上面那篇文章說是可以用於某張表的補充,比如用戶信息與用戶賬號密碼的分離,我個人的想法是可以將不必要的信息放在其他表裏面減少查詢所需要的消耗。感覺就是將一張原始的大表分爲子表和母表時用到的。

外鍵關係,一對多關係

從子表從母表中選出一條數據一一對應,但母表的這條數據還可以被其他子表數據選擇。從數據庫實現上這個是真的有外鍵了。

RelForeignKey:

type Post struct {
    ...
    User *User `orm:"rel(fk)"` // RelForeignKey relation
    ...
}

對應的反向關係RelReverseMany:

type User struct {
    ...
    Posts []*Post `orm:"reverse(many)"` // fk 的反向關係
    ...
}

上面的Post中,每個Post對應一個User,但是User可以對應多個Post。數據庫的實現如下:

create table `user`
    -- --------------------------------------------------
    --  Table Structure for `main.User`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `user` (
        `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY
    ) ENGINE=InnoDB;

create table `post`
    -- --------------------------------------------------
    --  Table Structure for `main.Post`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `post` (
        `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
        `user_id` integer NOT NULL
    ) ENGINE=InnoDB;

測試代碼:

package main

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

// Model Struct
type Post struct {
	Id int
    User *User `orm:"rel(fk)"` // RelForeignKey relation
}

type User struct {
	Id    int
	Posts []*Post `orm:"reverse(many)"` // fk 的反向關係
}


func init() {
    // set default database
	orm.RegisterDataBase("default", "mysql", "wty:97112500@tcp(127.0.0.1:3306)/test?charset=utf8")

    // register model
    orm.RegisterModel(new(User),new(Post))

    // create table
    orm.RunSyncdb("default", false, true)
}

func main() {
	
	o := orm.NewOrm()
	user := User{Id:1}
	post1 := Post{Id:1,User:&user}
	post2 := Post{Id:2,User:&user}
	storage := []*Post{&post1,&post2}
	user.Posts = storage
	id,err := o.Insert(&post1)
	fmt.Println(id,err)
	id,err = o.Insert(&post2)
	fmt.Println(id,err)
	id,err = o.Insert(&user)
	fmt.Println(id,err)
	fmt.Println("Finish")
}

實現場景上也並沒有外鍵,與上面類似,僅僅是實現了對應關係的主鍵。

多對多關係

RelManyToMany

type Post struct {
    ...
    Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation
    ...
}

對應的反向關係RelReverseMany:

type Tag struct {
    ...
    Posts []*Post `orm:"reverse(many)"`
    ...
}

這裏我就不實驗了,感覺上,表關係設置在數據庫層面並沒有太多的支持,可能在實際orm操作上有類似級聯刪除的機制,這個我之後在繼續進行測試。

更多的詳細設置還是要參考文檔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章