How to solve gorm Create() panic with "invalid memory address or nil pointer dereference"?

huangapple go评论89阅读模式
英文:

How to solve gorm Create() panic with "invalid memory address or nil pointer dereference"?

问题

我无法解决这个恐慌,因为我在Go语言中还是个新手,特别是在指针方面,所以请谅解。我追踪了恐慌消息,并发现恐慌发生在我的存储库包中的gorm Create()函数中。

这是我的数据库连接:

var (
	Instance *gorm.DB
)

func Connect() {
	if err := godotenv.Load(); err != nil {
		panic(err.Error())
	}

	dbUser := os.Getenv("DB_USERNAME")
	dbPass := os.Getenv("DB_PASSWORD")
	dbHost := os.Getenv("DB_HOST")
	dbName := os.Getenv("DB_NAME")

	dsn := fmt.Sprintf("%s:%s@tcp(%s:3303)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbHost, dbName)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})

	if err != nil {
		panic("Error connecting database: " + err.Error())
	}

	Instance = db
}

func CloseDB() {
	conn, err := Instance.DB()
	if err != nil {
		panic("Error closing database: " + err.Error())
	}

	conn.Close()
}

我的认证存储库:

type AuthRepository interface {
	Create(user *entity.User) *entity.User
}

type authConnection struct {
	db *gorm.DB
}

func NewAuthRepository(db *gorm.DB) AuthRepository {
	return &authConnection{db}
}

func (c *authConnection) Create(user *entity.User) *entity.User {
	newPassword, err := hashPassword(user)
	if err != nil {
		return nil
	}

	user.Password = newPassword
	result := c.db.Create(user)
	if result == nil {
		return nil
	}

	return user
}

使用认证存储库的认证服务:

var authRepository = repository.NewAuthRepository(database.Instance)

type AuthService struct {
	Repository repository.AuthRepository
}

func (s *AuthService) Register(user *entity.User) (*entity.User, error) {
	newUser := s.Repository.Create(user)
	if newUser == nil {
		return nil, errors.New("failed registering user")
	}

	return newUser, nil
}

func NewAuthService() *AuthService {
	return &AuthService{
		Repository: authRepository,
	}
}

最后是我的认证控制器:

var (
	authService = service.NewAuthService()
)

func RegisterUser(c *gin.Context) {
	var user entity.User

	if err := c.ShouldBindJSON(&user); err != nil {
		handler.BuildErrorResponse(c, err)
		return
	}

	record, err := authService.Register(&user)
	if err != nil {
		handler.BuildErrorResponse(c, err)
		return
	}

	handler.BuildCreatedResponse(c, record)
}

然后是错误信息:

runtime error: invalid memory address or nil pointer dereference
/usr/lib/golang/src/runtime/panic.go:212 (0x435eda)
        panicmem: panic(memoryError)
/usr/lib/golang/src/runtime/signal_unix.go:734 (0x44e892)
        sigpanic: panicmem()
/home/thoriqadillah/Development/Golang/pkg/mod/gorm.io/gorm@v1.23.6/finisher_api.go:18 (0x5d9226)
        (*DB).Create: if db.CreateBatchSize > 0 {
/home/thoriqadillah/Development/Golang/src/jwt/repository/auth_repository.go:28 (0xa19d04)
        (*authConnection).Create: result := c.db.Create(user)
/home/thoriqadillah/Development/Golang/src/jwt/service/auth_service.go:18 (0xa19ee1)
        (*AuthService).Register: newUser := s.Repository.Create(user)
/home/thoriqadillah/Development/Golang/src/jwt/controller/auth_controller.go:22 (0xa1a0ae)
        RegisterUser: record, err := authService.Register(&user)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9d9e39)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/recovery.go:101 (0x9d9e20)
        CustomRecoveryWithWriter.func1: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9d9013)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/logger.go:240 (0x9d8fd2)
        LoggerWithConfig.func1: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9cedcf)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:616 (0x9cedb5)
        (*Engine).handleHTTPRequest: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:572 (0x9ce879)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/lib/golang/src/net/http/server.go:2868 (0x7d7582)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/lib/golang/src/net/http/server.go:1933 (0x7d29ac)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/lib/golang/src/runtime/asm_amd64.s:1371 (0x46df60)
        goexit: BYTE    $0x90   // NOP

我猜这个错误来自于gorm的Create()函数。我尝试在认证存储库中使用fmt.Println(user)fmt.Println(&user)fmt.Println(*user)fmt.Println(&user)来调试,其中fmt.Println(&user)给出了内存地址。但是,即使我将Create(user)更改为Create(&user),它仍然不起作用。我尝试直接在控制器中使用database.Instance.Create(&user),它可以工作,但我想学习如何使我的代码更模块化。

英文:

I can't figure out how to solve this panic because I'm really new in Go, particularly in pointer, so bear with me. I tracked the panic message and found out that the panic is in gorm Create() in my repository package.

This is my database connection

var (
	Instance *gorm.DB
)

func Connect() {
	if err := godotenv.Load(); err != nil {
		panic(err.Error())
	}

	dbUser := os.Getenv("DB_USERNAME")
	dbPass := os.Getenv("DB_PASSWORD")
	dbHost := os.Getenv("DB_HOST")
	dbName := os.Getenv("DB_NAME")

	dsn := fmt.Sprintf("%s:%s@tcp(%s:3303)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbHost, dbName)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})

	if err != nil {
		panic("Error connecting database : " + err.Error())
	}

	Instance = db
}

func CloseDB() {
	conn, err := Instance.DB()
	if err != nil {
		panic("Error closing database : " + err.Error())
	}

	conn.Close()
}

My auth repository

type AuthRepository interface {
	Create(user *entity.User) *entity.User
}

type authConnection struct {
	db *gorm.DB
}

func NewAuthRepository(db *gorm.DB) AuthRepository {
	return &authConnection{db}
}

func (c *authConnection) Create(user *entity.User) *entity.User {
	newPassword, err := hashPassword(user)
	if err != nil {
		return nil
	}

	user.Password = newPassword
	result := c.db.Create(user)
	if result == nil {
		return nil
	}

	return user

}

My auth service that uses auth repository

var authRepository = repository.NewAuthRepository(database.Instance)

type AuthService struct {
	Repository repository.AuthRepository
}

func (s *AuthService) Register(user *entity.User) (*entity.User, error) {
	newUser := s.Repository.Create(user)
	if newUser == nil {
		return nil, errors.New("failed registering user")
	}

	return newUser, nil
}

func NewAuthService() *AuthService {
	return &AuthService{
		Repository: authRepository,
	}
}

Last one, my auth controller


var (
	authService = service.NewAuthService()
)

func RegisterUser(c *gin.Context) {
	var user entity.User

	if err := c.ShouldBindJSON(&user); err != nil {
		handler.BuildErrorResponse(c, err)
		return
	}

	record, err := authService.Register(&user)
	if err != nil {
		handler.BuildErrorResponse(c, err)
		return
	}

	handler.BuildCreatedResponse(c, record)
}

And then the error

runtime error: invalid memory address or nil pointer dereference
/usr/lib/golang/src/runtime/panic.go:212 (0x435eda)
        panicmem: panic(memoryError)
/usr/lib/golang/src/runtime/signal_unix.go:734 (0x44e892)
        sigpanic: panicmem()
/home/thoriqadillah/Development/Golang/pkg/mod/gorm.io/gorm@v1.23.6/finisher_api.go:18 (0x5d9226)
        (*DB).Create: if db.CreateBatchSize > 0 {
/home/thoriqadillah/Development/Golang/src/jwt/repository/auth_repository.go:28 (0xa19d04)
        (*authConnection).Create: result := c.db.Create(user)
/home/thoriqadillah/Development/Golang/src/jwt/service/auth_service.go:18 (0xa19ee1)
        (*AuthService).Register: newUser := s.Repository.Create(user)
/home/thoriqadillah/Development/Golang/src/jwt/controller/auth_controller.go:22 (0xa1a0ae)
        RegisterUser: record, err := authService.Register(&user)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9d9e39)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/recovery.go:101 (0x9d9e20)
        CustomRecoveryWithWriter.func1: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9d9013)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/logger.go:240 (0x9d8fd2)
        LoggerWithConfig.func1: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0x9cedcf)
        (*Context).Next: c.handlers[c.index](c)
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:616 (0x9cedb5)
        (*Engine).handleHTTPRequest: c.Next()
/home/thoriqadillah/Development/Golang/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:572 (0x9ce879)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/lib/golang/src/net/http/server.go:2868 (0x7d7582)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/lib/golang/src/net/http/server.go:1933 (0x7d29ac)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/lib/golang/src/runtime/asm_amd64.s:1371 (0x46df60)
        goexit: BYTE    $0x90   // NOP

I'm assuming that the error comes from gorm Create(). I tried debugging it with printing the the user parameter in auth repository inside the function with fmt.Println(user), fmt.Println(&user), fmt.Println(*user), and the fmt.Println(&user) gives memory address. But even I change the Create(user) with Create(&user) it still doesn't work. I tried directly use the gorm in the controller with database.Instance.Create(&user) and it worked, but I want to learn to make my code a bit modular

答案1

得分: 4

实际上,你在字符串中出现了一个错误:

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{

这是因为使用了 := 运算符,GO语言会创建一个新的局部变量 db 并使用它,而不是全局变量。
要修复这个问题,只需将这一行改为:

var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{

或者,在你的情况下,改为:

var err error
Instance, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
英文:

Actually, you got an error in the string:

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{

Because of operator :=
GO creates a new local variable db and uses it instead of the global one.
To fix this just change this line to:

var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{

Or, in your case, to this one:

var err error
Instance, err = gorm.Open(mysql.Open(dsn), &gorm.Config{

答案2

得分: 0

避免使用全局变量,并实现依赖注入

func NewAuthService(repository AuthRepository) *AuthService {
    return &AuthService{
        Repository: repository,
    }
}

func RegisterUser(c *gin.Context, authService AuthService) {
    ...
}
英文:

Avoid global variables and implement dependency injection.

func NewAuthService(repository AuthRepository) *AuthService {
    return &AuthService{
        Repository: repository,
    }
}

func RegisterUser(c *gin.Context, authService AuthService) {
    ...
}


huangapple
  • 本文由 发表于 2022年6月12日 20:13:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/72592083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定