簡體   English   中英

如何在Revel Controller中訪問Gorm?

[英]How to access Gorm in Revel Controller?

首先讓我說這些是我在Go玩弄的第幾天。

我正在嘗試使用像Gorm這樣的Revel框架:

app/controllers/gorm.go

package controllers

import (
    "fmt"
    "go-testapp/app/models"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
    "github.com/revel/revel"
)

var DB gorm.DB

func InitDB() {
    var err error
    DB, err = gorm.Open("mysql", "root:@/go-testapp?charset=utf8&parseTime=True")
    if err != nil {
        panic(err)
    }
    DB.LogMode(true)
    DB.AutoMigrate(models.User{})
}

type GormController struct {
    *revel.Controller
    DB *gorm.DB
}

app/controller/app.go

package controllers

import (
    "fmt"
    "go-bingo/app/models"

    _ "github.com/go-sql-driver/mysql"
    "github.com/revel/revel"
)

type App struct {
    GormController
}

func (c App) Index() revel.Result {
    user := models.User{Name: "Jinzhu", Age: 18}

    fmt.Println(c.DB)
    c.DB.NewRecord(user)

    c.DB.Create(&user)

    return c.RenderJson(user)
}

運行后導致:

runtime error: invalid memory address or nil pointer dereference第19行的runtime error: invalid memory address or nil pointer dereference c.DB.NewRecord(user)

它成功創建了具有自動遷移的數據表,但我不知道如何在控制器中使用Gorm。

正確方向的任何暗示?

重要的提示

它只是Revel 原始示例GORP的替代品。 它帶來了一些起源的陷阱。 此答案可用作原始答案的替代品。 但它並沒有解決陷阱。

請看看這個unswer和@MaxGabriel答案的評論 ,以解決陷阱。

我建議使用@ MaxGabriel的解決方案來保護您的應用程序免受某些慢速* DDoS攻擊。 並減少(在某些情況下)DB壓力。

原始答案

@rauyran權限 ,你必須在init函數內調用InitDB (進入controllers包)。

這里有完整的例子(太多):

/app
    /controllers
      app.go
      gorm.go
      init.go
    /models
      user.go
[...]

user.go

// models/user.go
package models

import  "time" // if you need/want

type User struct {          // example user fields
    Id                    int64
    Name                  string
    EncryptedPassword     []byte
    Password              string      `sql:"-"`
    CreatedAt             time.Time
    UpdatedAt             time.Time
    DeletedAt             time.Time     // for soft delete
}

gorm.go

//controllers/gorm.go
package controllers

import (
    "github.com/jinzhu/gorm"
    _ "github.com/lib/pq" // my example for postgres
    // short name for revel
    r "github.com/revel/revel"
    // YOUR APP NAME
    "yourappname/app/models"
    "database/sql"
)

// type: revel controller with `*gorm.DB`
// c.Txn will keep `Gdb *gorm.DB`
type GormController struct {
    *r.Controller
    Txn *gorm.DB
}

// it can be used for jobs
var Gdb *gorm.DB

// init db
func InitDB() {
    var err error
    // open db
    Gdb, err = gorm.Open("postgres", "user=uname dbname=udbname sslmode=disable password=supersecret")
    if err != nil {
        r.ERROR.Println("FATAL", err)
        panic( err )
    }
    Gdb.AutoMigrate(&models.User{})
    // unique index if need
    //Gdb.Model(&models.User{}).AddUniqueIndex("idx_user_name", "name")
}


// transactions

// This method fills the c.Txn before each transaction
func (c *GormController) Begin() r.Result {
    txn := Gdb.Begin()
    if txn.Error != nil {
        panic(txn.Error)
    }
    c.Txn = txn
    return nil
}

// This method clears the c.Txn after each transaction
func (c *GormController) Commit() r.Result {
    if c.Txn == nil {
        return nil
    }
    c.Txn.Commit()
    if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
        panic(err)
    }
    c.Txn = nil
    return nil
}

// This method clears the c.Txn after each transaction, too
func (c *GormController) Rollback() r.Result {
    if c.Txn == nil {
        return nil
    }
    c.Txn.Rollback()
    if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
        panic(err)
    }
    c.Txn = nil
    return nil
}

app.go

package controllers

import(
    "github.com/revel/revel"
    "yourappname/app/models"
)

type App struct {
    GormController
}

func (c App) Index() revel.Result {
    user := models.User{Name: "Jinzhup"}
    c.Txn.NewRecord(user)
    c.Txn.Create(&user)
    return c.RenderJSON(user)
}

init.go

package controllers
import "github.com/revel/revel"

func init() {
    revel.OnAppStart(InitDB) // invoke InitDB function before
    revel.InterceptMethod((*GormController).Begin, revel.BEFORE)
    revel.InterceptMethod((*GormController).Commit, revel.AFTER)
    revel.InterceptMethod((*GormController).Rollback, revel.FINALLY)
}

正如您所看到的,這就像為GORM修改了Revel的預訂。

對我來說很好。 結果:

{
  "Id": 5,
  "Name": "Jinzhup",
  "EncryptedPassword": null,
  "Password": "",
  "CreatedAt": "2014-09-22T17:55:14.828661062+04:00",
  "UpdatedAt": "2014-09-22T17:55:14.828661062+04:00",
  "DeletedAt": "0001-01-01T00:00:00Z"
}

您的錯誤是由於您尚未初始化c.DB數據庫變量引起的,它仍然是零。

在您的controllers / init.go文件中,確保您正在調用revel.OnAppStart(InitDB)。 它應該看起來像這樣:

package controllers

import "github.com/revel/revel"

func init() {
    revel.OnAppStart(InitDB)
    // maybe some other init things
}

根據他的建議,這個答案來自@ IvanBlack的回答。 他的版本是Revel示例代碼的直接翻譯,但是我們發現了這個答案修復的原始代碼的一些問題。 它的主要變化是:

  1. 整個HTTP請求不再由數據庫事務包裝。 包裝整個HTTP請求會使事務打開的時間遠遠超過必要的時間,這可能會導致許多問題:

    • 您的事務將保留數據庫上的鎖,並且許多持有鎖的事務可能導致死鎖
    • 使用了更多的數據庫資源
    • 如果您的HTTP請求主要不受SQL數據庫訪問限制,則會放大這些問題。 例如,如果您的HTTP請求在發出外部HTTP請求或訪問其他數據庫時阻塞了30秒,那么這些服務的減速可能會影響您的SQL數據庫。
  2. 由於在HTTP請求結束時不再自動檢查事務是否存在錯誤,因此只要進行數據庫插入,就會檢查錯誤。 這也更正確:想象一下,在將User結構插入數據庫之后,然后將User.Id存儲在另一個數據庫中,如Redis。 如果數據庫插入工作正常,但是如果它失敗並且您沒有立即檢查錯誤,則可以將默認的int64值0插入Redis(之后僅回滾SQL事務)。

/app
    /controllers
      app.go
      gorm.go
      init.go
    /models
      user.go
[...]

user.go

// models/user.go
package models

import  "time" // if you need/want

type User struct {          // example user fields
    Id                    int64
    Name                  string
    EncryptedPassword     []byte
    Password              string      `sql:"-"`
    CreatedAt             time.Time
    UpdatedAt             time.Time
    DeletedAt             time.Time     // for soft delete
}

gorm.go

//controllers/gorm.go
package controllers

import (
    "github.com/jinzhu/gorm"
    _ "github.com/lib/pq" // my example for postgres
    // short name for revel
    r "github.com/revel/revel"
)

// type: revel controller with `*gorm.DB`
type GormController struct {
    *r.Controller
    DB *gorm.DB
}

// it can be used for jobs
var Gdb *gorm.DB

// init db
func InitDB() {
    var err error
    // open db
    Gdb, err = gorm.Open("postgres", "user=USERNAME dbname=DBNAME sslmode=disable")
    Gdb.LogMode(true) // Print SQL statements
    if err != nil {
        r.ERROR.Println("FATAL", err)
        panic(err)
    }
}

func (c *GormController) SetDB() r.Result {
    c.DB = Gdb
    return nil
}

init.go

package controllers
import "github.com/revel/revel"

func init() {
    revel.OnAppStart(InitDB) // invoke InitDB function before
    revel.InterceptMethod((*GormController).SetDB, revel.BEFORE)
}

app.go

package controllers

import(
    "github.com/revel/revel"
    "yourappname/app/models"
)

type App struct {
    GormController
}

func (c App) Index() revel.Result {
    user := models.User{Name: "Jinzhup"} // Note: In practice you should initialize all struct fields
    if err := c.DB.Create(&user).Error; err != nil {
        panic(err)
    }
    return c.RenderJSON(user)
}

結果:

{
  "Id": 5,
  "Name": "Jinzhup",
  "EncryptedPassword": null,
  "Password": "",
  "CreatedAt": "2014-09-22T17:55:14.828661062+04:00",
  "UpdatedAt": "2014-09-22T17:55:14.828661062+04:00",
  "DeletedAt": "0001-01-01T00:00:00Z"
}

或者你需要傳遞指向AutoMigrate的指針?

 DB.AutoMigrate(&models.User{})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM