質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Q&A

解決済

1回答

2211閲覧

「Go言語」 PostgreSQLにデータを保存できません。

uk_63

総合スコア29

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

0グッド

0クリップ

投稿2019/02/15 13:51

編集2019/02/15 23:35

はじめに

質問を見てくださりありがとう御座います。
現在、Go言語の練習として、クライアント側のフォームからJSONを送信、サーバー側(Go)で受け取って、データベース(PostgreSQL)で保存させようとしています。
JSONを受け取ってPostgreSQLに接続までは成功しているのですが、保存が出来ません。

下記のパニックが出てました。

http: panic serving 127.0.0.1:50371: runtime error: invalid memory address or nil pointer dereference

構造体

2つあります。RoomPost です。
Room has_many Post という関係性です。

ここでは、まずRoomだけを作成してみようということで実践していましたが、DBへの保存のところでエラーが出ています。

コード(質問用に整形しています)

go

1// package, import 部分は省略しています。 2 3 4// Room ・誰でも投稿できる ・has_many Post 5type Room struct { 6 ID int `json:"id"` 7 Title string `json:"title"` 8 Content string `json:"content"` 9 CreatedAt time.Time `json:"created_at"` 10 Posts []Post `json:"posts"` 11} 12 13var postgreSQL *sql.DB 14 15func init() { 16 postgreSQL, err := sql.Open("postgres", "user=user dbname=db password=pw sslmode=disable") 17 if err != nil { 18 panic(err) 19 } 20 defer postgreSQL.Close() 21 22 err = postgreSQL.Ping() 23 if err != nil { 24 panic(err) 25 } 26 27// 接続成功する。 28 fmt.Print("Successfully connected!\n") 29} 30 31// SQLRoomCreate [ Create a new room ] 32func (room *Room) SQLRoomCreate() (err error) { 33 fmt.Print("\nRoomCreate is Starting!!\n\n") 34 statement := "insert into rooms (title, content) values ($1, $2) returning id, created_at" 35 36// ここでエラーが発生している!! 37 stmt, err := postgreSQL.Prepare(statement) 38 if err != nil { 39 return 40 } 41 defer stmt.Close() 42 err = stmt.QueryRow(room.Title, room.Content).Scan(&room.ID, &room.CreatedAt) 43 fmt.Print("RoomCreate is DONE") 44 return 45} 46 47func roomCreate(w http.ResponseWriter, r *http.Request) { 48 if r.Method != "POST" { 49 fmt.Print("不正なメソッドです。\n") 50 fmt.Printf("Method : \n%v\n", r.Method) 51 w.WriteHeader(http.StatusBadRequest) 52 return 53 } 54 55 if r.Header.Get("Content-Type") != "application/json" { 56 fmt.Print("JSONではありません。\n") 57 fmt.Printf("Response Header : \n%v\n", r.Header.Get("Content-Type")) 58 w.WriteHeader(http.StatusBadRequest) 59 return 60 } 61 62 //To allocate slice for request body 63 length, err := strconv.Atoi(r.Header.Get("Content-Length")) 64 if err != nil { 65 fmt.Printf("Content-Length : \n%v\n", r.Header.Get("Content-Length")) 66 fmt.Print("エラーが発生しました。\n") 67 log.Fatal(err) 68 w.WriteHeader(http.StatusInternalServerError) 69 return 70 } 71 72 // Read メソッドが []byte を読み込むことになっている 73 body := make([]byte, length) 74 _, err = r.Body.Read(body) 75 if err != nil && err != io.EOF { 76 fmt.Print("エラーが発生しました。\n") 77 log.Fatal(err) 78 w.WriteHeader(http.StatusInternalServerError) 79 return 80 } 81 82 // 受け取ったJSONを解析して構造体 Room にマッピング 83 // []byteをlength 分だけパースする 84 var room model.Room 85 err = json.Unmarshal(body[:length], &room) 86 if err != nil { 87 fmt.Print("エラーが発生しました。\n") 88 log.Fatal(err) 89 w.WriteHeader(http.StatusInternalServerError) 90 return 91 } 92 93 err = room.SQLRoomCreate() 94 if err != nil { 95 fmt.Printf("*\nERROR in room.go\n*\n") 96 log.Fatal(err) 97 w.WriteHeader(http.StatusInternalServerError) 98 return 99 } 100 w.WriteHeader(http.StatusOK) 101 http.Redirect(w, r, "/room", 302) 102} 103 104func main() { 105 mux := http.NewServeMux() 106 107 files := http.FileServer(http.Dir("assets")) 108 mux.Handle("/static/", http.StripPrefix("/static/", files)) 109 110 mux.HandleFunc("/", homeIndex) 111 mux.HandleFunc("/room/new", roomCreate) 112 113 server := http.Server{ 114 Addr: "127.0.0.1:8080", 115 Handler: mux, 116 } 117 log.Fatal(server.ListenAndServe()) 118} 119

コード(回答を受けて編集ました)

指摘を受けた部分を試してみましたが、同じくエラーが出ています。

http: panic serving 127.0.0.1:59381: runtime error: invalid memory address or nil pointer dereference

ログを見たところ、SQLRoomCreateという関数のところでエラーがでていると思われます。誤っている箇所がないか確認していただきたいです。よろしくおねがいします!

go

1// package, import 部分は省略しています。 2 3 4// Room ・誰でも投稿できる ・has_many Post 5type Room struct { 6 ID int `json:"id"` 7 Title string `json:"title"` 8 Content string `json:"content"` 9 CreatedAt time.Time `json:"created_at"` 10 Posts []Post `json:"posts"` 11} 12 13var postgreSQL *sql.DB 14// ここでDBへの接続情報入力 15const ( 16 host = "localhost" 17 port = 8080 18 user = "user" 19 password = "pw" 20 dbname = "database" 21) 22 23func init() { 24 // 変数に入れました。 25 psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", 26 host, port, user, password, dbname) 27 // それを代入しました。 28 postgreSQL, err := sql.Open("postgres", psqlInfo) 29 // init 内では使っていなためにエラーが出る。それの回避でアンダースコア。 30 var _ = postgreSQL 31 if err != nil { 32 panic(err) 33 } 34 35 // ColseしているためにDBに接続していないと予想。 36 37 // defer postgreSQL.Close() 38 39 // err = postgreSQL.Ping() 40 // if err != nil { 41 // panic(err) 42 // } 43 44 fmt.Print("Successfully connected!\n") 45} 46 47 48// SQLRoomCreate [ Create a new room ] 49func (room *Room) SQLRoomCreate() (err error) { 50 fmt.Print("\nRoomCreate is Starting!!\n\n") 51 statement := "insert into rooms (title, content) values ($1, $2) returning id, created_at" 52 53/////////////////////////// 54// ここでエラーが発生している!!?? 55////////////////////////// 56 57 stmt, err := postgreSQL.Prepare(statement) 58 if err != nil { 59 return 60 } 61 defer stmt.Close() 62 err = stmt.QueryRow(room.Title, room.Content).Scan(&room.ID, &room.CreatedAt) 63 fmt.Print("RoomCreate is DONE") 64 return 65} 66 67func roomCreate(w http.ResponseWriter, r *http.Request) { 68 if r.Method != "POST" { 69 fmt.Print("不正なメソッドです。\n") 70 fmt.Printf("Method : \n%v\n", r.Method) 71 w.WriteHeader(http.StatusBadRequest) 72 return 73 } 74 75 if r.Header.Get("Content-Type") != "application/json" { 76 fmt.Print("JSONではありません。\n") 77 fmt.Printf("Response Header : \n%v\n", r.Header.Get("Content-Type")) 78 w.WriteHeader(http.StatusBadRequest) 79 return 80 } 81 82 //To allocate slice for request body 83 length, err := strconv.Atoi(r.Header.Get("Content-Length")) 84 if err != nil { 85 fmt.Printf("Content-Length : \n%v\n", r.Header.Get("Content-Length")) 86 fmt.Print("エラーが発生しました。\n") 87 log.Fatal(err) 88 w.WriteHeader(http.StatusInternalServerError) 89 return 90 } 91 92 // Read メソッドが []byte を読み込むことになっている 93 body := make([]byte, length) 94 _, err = r.Body.Read(body) 95 if err != nil && err != io.EOF { 96 fmt.Print("エラーが発生しました。\n") 97 log.Fatal(err) 98 w.WriteHeader(http.StatusInternalServerError) 99 return 100 } 101 102 // 受け取ったJSONを解析して構造体 Room にマッピング 103 // []byteをlength 分だけパースする 104 var room model.Room 105 err = json.Unmarshal(body[:length], &room) 106 if err != nil { 107 fmt.Print("エラーが発生しました。\n") 108 log.Fatal(err) 109 w.WriteHeader(http.StatusInternalServerError) 110 return 111 } 112 113 err = room.SQLRoomCreate() 114 if err != nil { 115 fmt.Printf("*\nERROR in room.go\n*\n") 116 log.Fatal(err) 117 w.WriteHeader(http.StatusInternalServerError) 118 return 119 } 120 w.WriteHeader(http.StatusOK) 121 http.Redirect(w, r, "/room", 302) 122} 123 124func main() { 125 mux := http.NewServeMux() 126 127 files := http.FileServer(http.Dir("assets")) 128 mux.Handle("/static/", http.StripPrefix("/static/", files)) 129 130 mux.HandleFunc("/", homeIndex) 131 mux.HandleFunc("/room/new", roomCreate) 132 133 server := http.Server{ 134 Addr: "127.0.0.1:8080", 135 Handler: mux, 136 } 137 log.Fatal(server.ListenAndServe()) 138} 139

質問

①タイムスタンプはいつ押すべきなのかわからないため、created_atにデータを入れる処理を書いていません。いつその処理を書くべきですか? もしくはDB保存時に打刻されますか?

②プリペアドステートメントは間違っていないかチェックしていただきたいです。書籍を参考にしながらかいたので間違っているかもしれません。

どうかこのエラー解決に協力してください。お願いします!

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ぱっと見た感じですが提示しているコードだけ見ると func initに問題があるのではないでしょうか?

とりあえずコードにコメントしておきます(間違ってたらすみません)

Go

1var postgreSQL *sql.DB 2 3func init() { 4 // := は宣言と代入をいっぺんに行うので上で宣言している以上ここで使うのは違う気がする 5 postgreSQL, err := sql.Open("postgres", "user=user dbname=db password=pw sslmode=disable") 6 if err != nil { 7 panic(err) 8 } 9 // ここが問題 10 // deferは全ての実行が終わると最後に呼び出されるのでinit関数の実行が終了した時点で 11 // DBとの接続をクローズしてしまっている可能性大 12 defer postgreSQL.Close() 13 14 err = postgreSQL.Ping() 15 if err != nil { 16 panic(err) 17 } 18 19// 接続成功する。 20 fmt.Print("Successfully connected!\n") 21}

2/17 追記

必要な時にinit関数呼んであげてdaoをポインタで返せばいいかと思います
なるべく元の物に変更を加えないようにしたので正直イケてないですが恐らく動くかと

Go

1type Room struct { 2 ID int `json:"id"` 3 Title string `json:"title"` 4 Content string `json:"content"` 5 CreatedAt time.Time `json:"created_at"` 6 Posts []Post `json:"posts"` 7} 8 9const ( 10 host = "localhost" 11 port = 8080 12 user = "user" 13 password = "pw" 14 dbname = "database" 15) 16 17func init() (*sql.DB, error) { 18 psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", 19 host, port, user, password, dbname) 20 21 postgreSQL, err := sql.Open("postgres", psqlInfo) 22 23 if err != nil { 24 return nil, err 25 } 26 27 fmt.Print("Successfully connected!\n") 28 29 return &postgreSQL, nil 30} 31 32func (room *Room) SQLRoomCreate() (err error) { 33 postgreSQL, err := init() 34 35 if err != nil { 36 panic(err) 37 } 38 39 defer postgreSQL.Close() 40 41 err = postgreSQL.Ping() 42 43 if err != nil { 44 panic(err) 45 } 46 47 fmt.Print("\nRoomCreate is Starting!!\n\n") 48 statement := "insert into rooms (title, content) values ($1, $2) returning id, created_at" 49 50 stmt, err := postgreSQL.Prepare(statement) 51 52 if err != nil { 53 return 54 } 55 56 defer stmt.Close() 57 err = stmt.QueryRow(room.Title, room.Content).Scan(&room.ID, &room.CreatedAt) 58 fmt.Print("RoomCreate is DONE") 59 60 return nil 61} 62 63func roomCreate(w http.ResponseWriter, r *http.Request) { 64 if r.Method != "POST" { 65 fmt.Print("不正なメソッドです。\n") 66 fmt.Printf("Method : \n%v\n", r.Method) 67 w.WriteHeader(http.StatusBadRequest) 68 69 return 70 } 71 72 if r.Header.Get("Content-Type") != "application/json" { 73 fmt.Print("JSONではありません。\n") 74 fmt.Printf("Response Header : \n%v\n", r.Header.Get("Content-Type")) 75 w.WriteHeader(http.StatusBadRequest) 76 77 return 78 } 79 80 length, err := strconv.Atoi(r.Header.Get("Content-Length")) 81 82 if err != nil { 83 fmt.Printf("Content-Length : \n%v\n", r.Header.Get("Content-Length")) 84 fmt.Print("エラーが発生しました。\n") 85 log.Fatal(err) 86 w.WriteHeader(http.StatusInternalServerError) 87 88 return 89 } 90 91 body := make([]byte, length) 92 _, err = r.Body.Read(body) 93 94 if err != nil && err != io.EOF { 95 fmt.Print("エラーが発生しました。\n") 96 log.Fatal(err) 97 w.WriteHeader(http.StatusInternalServerError) 98 99 return 100 } 101 102 var room model.Room 103 104 err = json.Unmarshal(body[:length], &room) 105 106 if err != nil { 107 fmt.Print("エラーが発生しました。\n") 108 log.Fatal(err) 109 w.WriteHeader(http.StatusInternalServerError) 110 111 return 112 } 113 114 err = room.SQLRoomCreate() 115 116 if err != nil { 117 fmt.Printf("*\nERROR in room.go\n*\n") 118 log.Fatal(err) 119 w.WriteHeader(http.StatusInternalServerError) 120 121 return 122 } 123 124 w.WriteHeader(http.StatusOK) 125 http.Redirect(w, r, "/room", 302) 126} 127 128func main() { 129 mux := http.NewServeMux() 130 131 files := http.FileServer(http.Dir("assets")) 132 mux.Handle("/static/", http.StripPrefix("/static/", files)) 133 134 mux.HandleFunc("/", homeIndex) 135 mux.HandleFunc("/room/new", roomCreate) 136 137 server := http.Server{ 138 Addr: "127.0.0.1:8080", 139 Handler: mux, 140 } 141 142 log.Fatal(server.ListenAndServe()) 143}

2/17追記 2

postgresの環境を作るのが面倒なので検証していませんがDBアクセスに必要な前準備の部分だけ作ってみました
これを元に必要な処理の追加などやってみてはいかがでしょうか?
ちなみにdbのポートはサーバーポートと同じではダメです。

Go

1// main.go 2 3package main 4 5import ( 6 "github.com/yourname/projectname/controller" 7 "log" 8 "net/http" 9) 10 11func main() { 12 mux := http.NewServeMux() 13 14 controller.NewRouter(mux) 15 log.Fatal(http.ListenAndServe(":8080", mux)) 16}

Go

1// router.go 2 3package controller 4 5import ( 6 "net/http" 7) 8 9func NewRouter(mux *http.ServeMux) { 10 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 11 w.WriteHeader(200) 12 w.Write([]byte("Index")) 13 }) 14 15 mux.HandleFunc("/room/new", NewRoom) 16}

Go

1// room.go 2 3package controller 4 5import ( 6 _ "github.com/lib/pq" 7 8 "database/sql" 9 "net/http" 10) 11 12func NewRoom(w http.ResponseWriter, r *http.Request) { 13 db, err := sql.Open("postgres", "host=127.0.0.1 port=5555 user=user password=pw dbname=database") 14 15 defer db.Close() 16 17 if err != nil { 18 http.Error(w, err.Error(), 500) 19 } 20 21 // リクエストのパース(JSONから値を抜き出す) 22 23 // データベースの操作(書き込みとか) 24}

投稿2019/02/15 20:17

編集2019/02/17 00:18
teikoku-penguin

総合スコア314

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

uk_63

2019/02/15 23:37

回答ありがとうございました。 コード(回答を受けて編集しました)を追記しましたので、見ていただけないでしょうか? よろしくおねがいします!
uk_63

2019/02/16 17:17

回答ありがとうございます。 いただいた回答を早速ためそうとしましたが、init 関数の引数と戻り値がないとエラーがでてしまいました。どのように引数と戻り値をせっていするのが正しいですか? また必要なときにinit 関数を呼ぶというのは、必要なときにDBへ接続するということですか? お忙しい中、私の質問にお付き合いくださりありがとうございます。引き続きよろしくおねがいします。
teikoku-penguin

2019/02/17 00:15

> 必要なときにinit 関数を呼ぶというのは、必要なときにDBへ接続するということですか? リクエストがDBアクセスを行う処理に対する物であればアクセスされたタイミングで接続して処理が終わったら切断する といった感じです とりあえずコードサンプル追加しました 雛形的なものを作ったので必要な処理をコメントの部分に入れたりして試してみて下さい。
uk_63

2019/02/17 07:58

回答ありがとうございます。 > リクエストがDBアクセスを行う処理に対する物であればアクセスされたタイミングで接続して処理が終わったら切断する この処理の実装に取り組んでみます。 返事遅れますが、都合が許す限りお付き合いください。 よろしくおねがいします!
uk_63

2019/02/26 15:38

ベストアンサー遅れてすみません。 質問にお付き合いしてくださり、また丁寧な回答をありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問