How do I access the Gorm in the Revel Controller?

Let me start by saying that these are my first couple of days playing Go.

I am trying to use Revel framework with Gorm like this:

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

      

After running its result:

runtime error: invalid memory address or nil pointer dereference

on line 19 c.DB.NewRecord(user)

It creates data successfully with automation, but I have no idea how to use Gorm in the controller.

Any clues in the right direction?

+3


source to share


4 answers


Important note

it is just a replacement for the GORP original Revel example . And this is due to some of the pitfalls of origin. This answer can be used as a replacement for the original one. But that doesn't solve the pitfalls.

Please see the comments of this answer and @ MaxGabriel's answer which resolves the pitfalls.

I would recommend using @MaxGabriel's solution to protect your application from some kind of slow DDoS attacks. And reduce (in some cases) the OBD pressure.

Original Answer

@rauyran rights , you must call the function InitDB

inside init

(in the package controllers

).

Full example here (too many):

Wood

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

      

As you can see, this is similar to Revel Booking modified for GORM.

Works great for me. Result:

{
  "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"
}

      

+8


source


Your error is caused by not initializing the c.DB database variable, it is still zero.

In your controller file /init.go make sure you call revel.OnAppStart (InitDB). It should look something like this:



package controllers

import "github.com/revel/revel"

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

      

+2


source


This answer is derived from @ IvanBlack's answer, as suggested by him. Its version is a direct translation of the Revel example code, but we have identified some issues with the source code, which is fixed by this answer. Major changes:

  • The entire HTTP request is no longer completed with a database transaction. Wrapping the entire HTTP request takes much longer than necessary to open the transaction, which can cause a number of problems:

    • Your transactions will store locks in the database, and many transactions containing locks can lead to locks.
    • Additional database resources are being used
    • These problems increase if your HTTP requests are mostly not restricted to SQL database access. For example. if your HTTP request is blocked for 30 seconds while making an external HTTP request or accessing another database, slowing down these services could affect your SQL database.
  • Since the transaction is no longer automatically checked for errors at the end of the HTTP request, errors are checked immediately after the database is inserted. This is also more correct: imagine that after inserting the User structure into the database, you then save it User.Id

    to another database, such as Redis. It would be nice if the database insert worked, but if it fails and you don't check for the error right away, you should insert the default int64 from 0 into Redis (before rolling back only the SQL transaction later).

Wood

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

      

Result:

{
  "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"
}

      

+1


source


OR do you need to pass a pointer to AutoMigrate?

 DB.AutoMigrate(&models.User{})

      

-1


source







All Articles