thepoy 发表于 2020-7-18 22:34

【gin+gorm】向表中添加数据时报invalid memory address or nil pointer dereference

本帖最后由 thepoy 于 2020-7-19 10:07 编辑

model
```go
type User struct {
      gorm.Model
      Username      string   `json:"username" form:"username" binding:"required" gorm:"type:varchar(20);not null;unique"`
      Password      string   `json:"password" form:"password" binding:"required" gorm:"not null"`
      Email         string   `json:"email" form:"email" binding:"required" gorm:"type:varchar(60);not null;unique"`
      Phone         string   `json:"phone" form:"phone" binding:"required" gorm:"type:varchar(20);not null;unique"`
      RegisterIP    string   `json:"register_ip" gorm:"type:varchar(15);not null"`
      LastLoginTime *time.Time `json:"last_login_time"`
      LastLoginIP   string   `json:"last_login_ip" gorm:"type:varchar(15)"`
      Blogs         []Blog
}
```
db
```go
var db *gorm.DB

func init() {
      db, err := gorm.Open("mysql", "go:000aaa@tcp(127.0.0.1:3306)/blog")
      if err != nil {
                panic("Connecting database failed:" + err.Error())
      }
      db.DB().SetMaxOpenConns(100)
      db.DB().SetMaxIdleConns(20)
      db.AutoMigrate(&models.Blog{}, &models.User{}, &models.BlogType{})
      db.Model(&models.Blog{}).AddUniqueIndex("title", "user_id", "blog_type_id")
}
func GetDB() *gorm.DB {
      return db
}
```
handler
```go
func Regsiter(c *gin.Context) {
      var form models.User
      if err := c.ShouldBindWith(&form, binding.Form); err != nil {
                c.JSON(http.StatusBadRequest, gin.H{
                        "error": err.Error(),
                })
                return
      }
      db := utils.GetDB()
      
      // 定义一个user实例
      user := models.User{
                Username:   form.Username,
                Password:   form.Password,
                Email:      form.Email,
                Phone:      form.Phone,
                RegisterIP: c.Request.RemoteAddr,
      }
      user.CreatedAt = time.Now()
      
      fmt.Printf("%p", &user)

      db.Create(&user)   // 此行报错,但&user应该是能查到的有效指针才对,是user定义时出问题了吗?

      c.JSON(http.StatusCreated, gin.H{
                "msg":      "register successful",
                "username": form.Username,
      })
}
```

运行后访问register,填好required的数据,报空指针或无效指针:
```shell
2020/07/18 22:08:12 2020/07/18 - 22:08:12 panic recovered:
POST /register HTTP/1.1
Host: localhost:8080
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 434
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFnAqpmWULl4CibNl
Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Postman-Token: d06a2054-81f0-0c9c-7ba7-03ab09a993b2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: none
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36


runtime error: invalid memory address or nil pointer dereference
/usr/local/go/src/runtime/panic.go:212 (0x44aa19)
      panicmem: panic(memoryError)
/usr/local/go/src/runtime/signal_unix.go:695 (0x44a868)
      sigpanic: panicmem()
/home/thepoy/go/pkg/mod/github.com/jinzhu/gorm@v1.9.14/main.go:848 (0x57ccd6)
      (*DB).clone: dialect:         newDialect(s.dialect.GetName(), s.db),
/home/thepoy/go/pkg/mod/github.com/jinzhu/gorm@v1.9.14/main.go:204 (0x575d8e)
      (*DB).NewScope: dbClone := s.clone()
/home/thepoy/go/pkg/mod/github.com/jinzhu/gorm@v1.9.14/main.go:482 (0x579ed2)
      (*DB).Create: scope := s.NewScope(value)
/media/thepoy/软件/OneDrive - WULEL/code/go/src/go_blog_api/api/user.go:34 (0x9794c3)
      Regsiter: db.Create(&user)
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 (0x96449a)
      (*Context).Next: c.handlers(c)
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/recovery.go:83 (0x977b9f)
      RecoveryWithWriter.func1: c.Next()
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 (0x96449a)
      (*Context).Next: c.handlers(c)
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/logger.go:241 (0x976cd0)
      LoggerWithConfig.func1: c.Next()
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 (0x96449a)
      (*Context).Next: c.handlers(c)
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:409 (0x96e275)
      (*Engine).handleHTTPRequest: c.Next()
/home/thepoy/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:367 (0x96d98c)
      (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/go/src/net/http/server.go:2836 (0x78b082)
      serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/go/src/net/http/server.go:1924 (0x7869eb)
      (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/go/src/runtime/asm_amd64.s:1373 (0x464270)
      goexit: BYTE      $0x90      // NOP
```

添加数据的代码,在单独测试时能够正常添加,放到gin里就报这个错。
请问是哪里的问题呢?

540558233 发表于 2020-7-19 00:21

你的db实例有问题吧,db, err := gorm.Open("mysql", "go:000aaa@tcp(127.0.0.1:3306)/blog")这一行的 := 赋值的变量db是局部变量,不能赋值给上面的全局变量,用 = 号直接赋值试试

lm93129 发表于 2020-7-19 01:45



初始化数据库这块应该这样写,定一个全局可用的DB指针,初始化db之后赋值给这个全局可用的DB
var DB *gorm.DB

// Database 在中间件中初始化mysql链接
func Database() {

        db, err := gorm.Open("mysql", "go:000aaa@tcp(127.0.0.1:3306)/blog")

        if err != nil {
                util.Log().Panic("连接数据库不成功", err)
        }

        if gin.Mode() == gin.ReleaseMode {
                db.LogMode(false)
        } else {
                db.LogMode(true)
        }

        db.SingularTable(true)
        //设置连接池
        //空闲
        db.DB().SetMaxIdleConns(50)
        //打开
        db.DB().SetMaxOpenConns(100)
        //超时
        db.DB().SetConnMaxLifetime(time.Second * 30)

        DB = db

        // 同步数据
        migration()
}


使用的时候,使用这个全局可以用的DB就可以了,使用的示例如下:

funcShow(id string) {
        var statistic model.AppManage
        // 这个model.DB 就是初始化的那个全局变量DB
        err := model.DB.Model(&statistic).
        Select("project_id ,COUNT(*)").
        Group("project_id").Where("created_at BETWEEN ? AND ?", server.StarTime, server.EndTime).
        Scan(&results).Error

        if err != nil {
                return serializer.DBErr("", err)
        }
}

myxy999 发表于 2020-7-19 07:39

lm93129 发表于 2020-7-19 01:45
初始化数据库这块应该这样写,定一个全局可用的DB指针,初始化db之后赋值给这个全局可用的DB

他写了一个专门获取db的方法,所以不是这个问题

myxy999 发表于 2020-7-19 08:07

我测试了下没啥问题啊
func GetDB() *gorm.DB {
      db, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/blog")
      if err != nil {
                panic("Connecting database failed:" + err.Error())
      }
      db.DB().SetMaxOpenConns(100)
      db.DB().SetMaxIdleConns(20)
      db.AutoMigrate(&User{})
      return db
}

type User struct {
      gorm.Model
      Username      string   `json:"username" form:"username" binding:"required" gorm:"type:varchar(20);not null;unique"`
      Password      string   `json:"password" form:"password" binding:"required" gorm:"not null"`
      Email         string   `json:"email" form:"email" binding:"required" gorm:"type:varchar(60);not null;unique"`
      Phone         string   `json:"phone" form:"phone" binding:"required" gorm:"type:varchar(20);not null;unique"`
      RegisterIP    string   `json:"register_ip" gorm:"type:varchar(15);not null"`
      LastLoginTime *time.Time `json:"last_login_time"`
      LastLoginIP   string   `json:"last_login_ip" gorm:"type:varchar(15)"`
}

func TestRegsiter(t *testing.T) {
      app := gin.Default()
      db := GetDB()
      app.POST("/regsiter", func(c *gin.Context) {
                var form User
                if err := c.ShouldBindWith(&form, binding.Form); err != nil {
                        c.JSON(http.StatusBadRequest, gin.H{
                              "error": err.Error(),
                        })
                        return
                }
                // 定义一个user实例
                user := User{
                        Username:   form.Username,
                        Password:   form.Password,
                        Email:      form.Email,
                        Phone:      form.Phone,
                        RegisterIP: c.Request.RemoteAddr,
                }
                user.CreatedAt = time.Now()

                fmt.Printf("%p", &user)

                db.Create(&user) // 此行报错,但&user应该是能查到的有效指针才对,是user定义时出问题了吗?

                c.JSON(http.StatusCreated, gin.H{
                        "msg":      "register successful",
                        "username": form.Username,
                })
      })
      _ = app.Run(":8888")
}

页: [1]
查看完整版本: 【gin+gorm】向表中添加数据时报invalid memory address or nil pointer dereference