英文:
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) {
...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论