dragonhong641016 2016-04-06 15:44
浏览 514

如何使用GORM(Go)处理级联操作

I'm testing out Go's GORM lib. I find this lib particularly useful and, step by step, I play with more and more complicated notions.

I'm facing the problem of cascading operation management.

On certain issues, the creator suggests to use the AfterDelete. The problem is : in the After/BeforeDelete functions, nested items are not present.

Is everyone have a good way to implement this ?

Here is my code (almost working if someone is discovering Gorm) :

package main

import (
    "time"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "fmt"
    "github.com/satori/go.uuid"
)

type Company struct {
    ID        string     `gorm:"primary_key;column:ID"`
    Name      string     `sql:"size:255;unique;index" gorm:"column:Name"`
    Employees []Employee // one-to-many relationship
    Address   Address    // one-to-one relationship
}

func (u Company) TableName() string {
    return "Company"
}
func (u Company) String() string {
    return fmt.Sprintf("ID: %s | Name: %s | Employees: %v | Address: %v ", u.ID, u.Name, u.Employees, u.Address)
}
func (u *Company) BeforeCreate(scope *gorm.Scope) error {
    scope.SetColumn("ID", uuid.NewV4().String())
    return nil
}
func (u *Company) BeforeDelete(scope *gorm.Scope) error {
    fmt.Println("BeforeDelete")
    fmt.Println(u)
    return nil
}
func (u *Company) AfterDelete(scope *gorm.Scope) error {
    fmt.Println("AfterDelete")
    fmt.Println(u)
    return nil
}

type Employee struct {
    ID        string        `gorm:"primary_key;column:ID"`
    FirstName        string    `gorm:"column:FirstName"`
    LastName         string    `gorm:"column:LastName"`
    SocialSecurityNo string    `gorm:"column:SocialSecurityNo"`
    DateOfBirth      time.Time `sql:"DEFAULT:current_timestamp" gorm:"column:DateOfBirth"`
    Deleted          bool      `sql:"DEFAULT:false" gorm:"column:Deleted"`
    CompanyID    string    `gorm:"column:Company_ID"`
    Roles []Role // one-to-many relationship
}
func (u Employee) TableName() string {
    return "Employee"
}
func (u Employee) String() string {
    return fmt.Sprintf("ID: %s | FirstName: %s | Roles: %v ", u.ID, u.FirstName, u.Roles)
}
func (u *Employee) BeforeCreate(scope *gorm.Scope) error {
    scope.SetColumn("ID", uuid.NewV4().String())
    return nil
}

type Role struct {
    Name string `gorm:"column:Name"`
    Code string `gorm:"column:Code"`
    EmployeeID string `gorm:"column:Employee_ID"`
}
func (u Role) TableName() string {
    return "Role"
}
func (u Role) String() string {
    return fmt.Sprintf("Name: %s | Code: %s", u.Name, u.Code)
}

type Address struct {
    Country  string `gorm:"column:Country"`
    City     string `gorm:"column:City"`
    PostCode string `gorm:"column:PostCode"`
    Line1    string `gorm:"column:Line1"`
    Line2    string `gorm:"column:Line2"`
    CompanyID    string `gorm:"column:Company_ID"`
}
func (u Address) TableName() string {
    return "Address"
}

func main() {
    db := getDBConnection()
    //If needed, you can create the file and schemas with the line below
    createTables(db)
    testCRUD(db)
}

func getDBConnection() (db *gorm.DB) {
    //Change the file location for your needs
    db, err := gorm.Open("sqlite3", `C:\Users\jbricout\Desktop\TestORM.db`)
    if err != nil {
        panic(err)
    }

    // Ping function checks the database connectivity
    err = db.DB().Ping()
    if err != nil {
        panic(err)
    }

    return db
}

func createTables(db *gorm.DB) {
    if err := db.CreateTable(&Company{}).Error; err != nil {
        checkErr(err)
    }
    if err := db.CreateTable(&Address{}).Error; err != nil {
        checkErr(err)
    }
    if err := db.CreateTable(&Employee{}).Error; err != nil {
        checkErr(err)
    }
    if err := db.CreateTable(&Role{}).Error; err != nil {
        checkErr(err)
    }
}

func testCRUD(db *gorm.DB) {
    sampleCompany := getInitializedCompany()

    fmt.Println("Insert...")
    if err := db.Create(&sampleCompany).Error; err != nil {
        checkErr(err)
    }
    fmt.Println("Insert done with id : ", sampleCompany.ID)

    fmt.Println("Find Only Company (Lazy load)...")
    var firstComp Company
    if err := db.Where("ID = ?", sampleCompany.ID).First(&firstComp).Error; err != nil {
        checkErr(err)
    }
    fmt.Println("Company : ", firstComp)
    fmt.Println("Find done")

    fmt.Println("Find Only Company (Eager load)...")
    var fullComp Company

    db.Preload("Employees.Roles").Preload("Address").First(&fullComp)
    if err := db.Where("ID = ?", sampleCompany.ID).First(&fullComp).Error; err != nil {
        checkErr(err)
    }
    fmt.Println("Company : ", fullComp)
    fmt.Println("Find done")

    fmt.Println("Update...")
    firstComp.Name = "Google Plus"
    if len(firstComp.Address.Country) > 0 {
        firstComp.Address.Country = "France"
    }

    if err := db.Save(&firstComp).Error; err != nil {
        checkErr(err)
    }
    fmt.Println("Update done")

    transaction := db.Begin()
    fmt.Println("Delete...")
    if err := transaction.Delete(&firstComp).Error; err != nil {
        checkErrTransaction(err, transaction)
    }

    transaction.Commit()
    fmt.Println("Delete done")
}

func getInitializedCompany() Company {
    return Company{
        Name: "Google",
        Address: Address{
            Country:  "USA",
            City:     "Moutain View",
            PostCode: "1600",
            Line1: "Cloverfield Lane, 32",
            Line2: "Apt 64",
        },
        Employees: []Employee{
            Employee{
                FirstName:        "John",
                LastName:         "Doe",
                SocialSecurityNo: "00-000-0000",
                Roles: []Role{
                    Role{
                        Name: "Metier 1",
                        Code: "MET1",
                    },
                    Role{
                        Name: "Metier 2",
                        Code: "MET2",
                    },
                },
            },
            Employee{
                FirstName:        "James",
                LastName:         "Dean",
                SocialSecurityNo: "00-000-0001",
                Roles: []Role{
                    Role{
                        Name: "Metier 1",
                        Code: "MET1",
                    },
                },
            },
            Employee{
                FirstName:        "Joan",
                LastName:         "Dutsch",
                SocialSecurityNo: "00-000-0002",
                Roles: []Role{
                    Role{
                        Name: "Metier 2",
                        Code: "MET3",
                    },
                },
            },
        },
    }
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

func checkErrTransaction(err error, transaction *gorm.DB) {
    transaction.Rollback()
    if err != nil {
        panic(err)
    }
}

Thanks

  • 写回答

3条回答 默认 最新

  • duanji9311 2016-04-11 09:48
    关注

    I have implemented this solution for responding to my problem :

    func DeleteContentCascade(content *Content, db *gorm.DB, debug bool) error {
    
      if debug {
          db = db.Debug()
      }
    
      for _, child := range content.Children {
          DeleteChildCascade(&child, db, debug) //Custom method like the current
      }
    
      if err := db.Delete(content).Error; err != nil {
          return err
      }
    
      return nil
    }
    

    For every "Item" File in my DB management, I have created a custom function DeleteCascade.

    I hope it'll help :)

    评论

报告相同问题?

悬赏问题

  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图