5

首先让我说这是我在围棋中玩耍的头几天。

我正在尝试将 Revel 框架与 Gorm 一起使用,如下所示:

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 行c.DB.NewRecord(user)

它使用自动迁移成功创建了数据表,但我不知道应该如何在我的控制器中使用 Gorm。

有正确方向的提示吗?

4

4 回答 4

11

重要的提示

它只是Revel原始示例GORP的替代品。它带有一些起源陷阱。这个答案可以用作原始答案的直接替代品。但这并不能解决陷阱。

请看一下这个 unswer 和@MaxGabriel解决陷阱的答案的评论。

我建议使用@MaxGabriel 的解决方案来保护您的应用程序免受某些缓慢* DDoS 攻击。并减少(在某些情况下)数据库压力。

原始答案

@rauyran rights,你必须调用InitDB内部init函数(进入controllers包)。

这里的完整示例(太多):

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

用户.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
}

应用程序.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)
}

初始化.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 的 Booking。

对我来说很好。结果:

{
  "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"
}
于 2014-09-22T14:19:31.030 回答
3

这个答案来自@IvanBlack 在他的建议下的回答。他的版本是 Revel 示例代码的直接翻译,但我们发现此答案修复的原始代码存在一些问题。它所做的主要变化是:

  1. 整个 HTTP 请求不再由数据库事务包装。包装整个 HTTP 请求会使事务的打开时间远远超过必要的时间,这可能会导致许多问题:

    • 您的事务将在数据库上持有锁,许多持有锁的事务可能会导致死锁
    • 使用更多的数据库资源
    • 如果您的 HTTP 请求主要不受 SQL 数据库访问的限制,那么这些问题就会被放大。例如,如果您的 HTTP 请求在发出外部 HTTP 请求或访问另一个数据库时阻塞 30 秒,那么这些服务的减速可能会影响您的 SQL 数据库。
  2. 因为在 HTTP 请求结束时不再自动检查事务是否有错误,所以一旦进行数据库插入,就会检查错误。这也更正确:想象一下,在将 User 结构插入数据库后,您将其存储User.Id在另一个数据库(如 Redis)中。如果数据库插入有效,这很好,但如果它失败并且您没有立即检查错误,您将在 Redis 中插入默认的 int64 值 0(在稍后仅回滚 SQL 事务之前)。

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

用户.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
}

初始化.go

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

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

应用程序.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"
}
于 2017-10-30T18:34:13.963 回答
2

您的错误是由于您尚未初始化 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
}
于 2014-08-05T15:25:58.097 回答
-1

或者您需要传递指向 AutoMigrate 的指针?

 DB.AutoMigrate(&models.User{})
于 2017-10-18T20:41:26.803 回答