#mysql #database #go #go-gorm
#Вперед
Вопрос:
До сих пор самой сложной частью Go было понимание того, как организовать код. На первый взгляд это кажется невероятно простым, но каждый раз, когда я пытаюсь что-то сделать, я сталкиваюсь с циклическим импортом или такими вещами, как «экспортированный запуск функции возвращает неэкспортированный тип models.dbStore, который может раздражать в использовании».
Используя следующий код, как мне вызвать db.Close()
или я действительно не понимаю, как я должен предоставлять базу данных своим моделям. Вот что у меня есть:
App.go
package app
import (
"database/sql"
// Comment
_ "github.com/mattn/go-sqlite3"
)
var (
// DB The database connection
db *sql.DB
)
// Setup Sets up the many many app settings
func Setup() {
d, err := sql.Open("sqlite3", "./foo.db")
if err != nil {
panic(err)
}
// TODO: How does the DB get closed?
// defer db.Close()
db = d
}
// GetDB Returns a reference to the database
func GetDB() *sql.DB {
return db
}
Users.go
package models
import (
"github.com/proj/org/app"
)
// User struct
type User struct {
ID int
}
// CreateUser Creates a user
func (u *User) CreateUser() (int64, error) {
// For the sake of brevity just make sure you can
// "connect" to the database
if err := app.GetDB().Ping(); err != nil {
panic(err)
}
return 1234, nil
}
main.go
package main
import (
"fmt"
"net/http"
_ "github.com/mattn/go-sqlite3"
"github.com/proj/org/app"
"github.com/proj/org/models"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "You are home")
}
func subscribeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Subscribing...")
u := models.User{}
u.CreateUser()
}
func main() {
fmt.Println("Running")
app.Setup()
http.HandleFunc("/", homeHandler)
http.HandleFunc("/subscribe", subscribeHandler)
err := http.ListenAndServe(":9090", nil)
if err != nil {
panic(err)
}
}
Я думал о том, чтобы сделать a app.Shutdown()
, но это не сработало бы для моего самого обычного варианта использования, который является CTRL-C. Казалось бы, если я не закрою базу данных, соединения с БД будут просто расти… Просто пытаюсь понять.
Комментарии:
1.По мере роста и развития проектов вы, вероятно, захотите немного перевернуть это с ног на голову. В нынешнем виде как ваш, так
app
иmain
пакеты связаны с базой данных, чего вы, вероятно, хотели бы избежать с точки зрения дизайна. Их разделение помогло бы решить некоторые из ваших проблем с зависимостями, но требует немного больше внимания к дизайну.2. @Adrian У вас есть какие-нибудь примеры приложений goto, которые показывают это разделение? Я просмотрел несколько, например, Netlify GoTrue API, но иногда мне трудно понять общую картину, потому что существует так много пакетов / абстракций.
3. Пример для развязки и использования правильного внедрения зависимостей: github.com/rameshsunkara/go-rest-api-example
Ответ №1:
Нет необходимости закрывать базу данных.
Возвращенная база данных безопасна для одновременного использования несколькими программами и поддерживает свой собственный пул незанятых соединений. Таким образом, функция Open должна вызываться только один раз. Редко требуется закрывать базу данных.
Из: https://golang.org/pkg/database/sql/#Open
Когда ваша программа завершает работу, любое открытое соединение закрывается, оно не остается открытым где-то в эфире, ожидая повторного запуска вашей программы, поэтому не беспокойтесь о том, что соединения «растут», когда вы нажимаете CTRL-C в своем приложении.
Однако, если вы все еще хотите закрыть его, вы можете просто экспортировать CloseDB
функцию так же, как вы это делаете с GetDB
.
App.go
// ...
func CloseDB() error {
return db.Close()
}
main.go
// ...
func main() {
// ...
app.Setup()
defer app.CloseDB()
// ...
}
Комментарии:
1. Спасибо @mkopriva Я бы подумал, что это зависит от конкретного драйвера, поэтому не подумал заглянуть туда. Также некоторые пропустили раскрытие метода, чтобы я мог отложить его вне
app
пакета.2. Не закрытие базы данных может привести к тайм-ауту соединений. Вы можете решить не закрывать его. Не имеет большого значения, но это проблема, если ваш процесс запускается и останавливается очень быстро.
3. итак, вместо того, чтобы каждый обработчик вызывал db.open() — должны ли они все проходить через общий экземпляр db?
4. @alex Им не нужно передавать это по кругу. Но им нужно поделиться им, и передача его — один из способов совместного использования.