英文:
When is right time to run Automigrate with GORM
问题
我理解你的问题是关于在API服务中使用GORM时是否应该将Automigrate从常规流程中移除并单独处理,以避免在每个API请求中都进行昂贵的数据库迁移操作。
根据你提供的GORM文档示例,大多数Go/GORM示例都在打开数据库连接后立即调用Automigrate方法。对于API服务来说,这将是一个昂贵且不必要的操作,因为它会在每个API请求中都执行一次。因此,我认为你的理解是正确的,应该将Automigrate从常规流程中移除,并单独处理。
你可以在API服务启动时执行一次数据库迁移操作,例如在应用程序初始化阶段或者在启动脚本中调用Automigrate方法。这样可以确保数据库的表结构与模型定义保持一致,而无需在每个API请求中都执行迁移操作。
希望这个回答对你有帮助!如果你还有其他问题,请随时提问。
英文:
Most Go/GORM examples I've seen show Automigrate being called immediately after opening the database connection, including GORM documentation here. For API services, this would be an expensive/wanted call with every API requests. So, I assume, for API services, Automigrate should be removed from regular flow and handled separately. Is my understanding correct?
From GORM Documentation
...
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&Product{})
...
答案1
得分: 2
这不会发生在每个API请求上。甚至远远不会发生。它只会在应用程序启动时发生,所以基本上是:在main
中连接到数据库,并在那里运行AutoMigrate
。将连接作为依赖项传递给处理程序/服务包/任何需要它的地方。HTTP处理程序可以直接在那里访问它。
基本上是这样的:
package main
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
fmt.Printf("Failed to connect to DB: %v", err)
os.Exit(1)
}
// 请参考下面的处理方式
fRepo := foo.New(db) // 所有的存储库都在这里
fRepo.Migrate() // 这处理迁移
// 创建请求处理程序
fHandler := handlers.NewFoo(fRepo) // 迁移已经处理过了
mux := http.NewServeMux()
mux.HandleFunc("/foo/list", fHandler.List) // 设置处理程序
// 启动服务器等...
}
将与数据库交互的代码放在某个包中,例如:
package foo
// 作为您使用的数据库连接接口
type Connection interface {
Create()
Find()
AutoMigrate(any)
}
type Foo struct {
db Connection
}
func New(db Connection) *Foo {
return &Foo{
db: db,
}
}
func (f *Foo) Migrate() {
f.db.AutoMigrate(&Stuff{}) // 所有此存储库处理的类型都在这里
}
func (f *Foo) GetAll() ([]Stuff, error) {
ret := []Stuff{}
res := f.db.Find(&ret)
return ret, res.Error
}
然后以合理的方式组织处理程序,并为它们提供存储库(即foo包的内容):
package handlers
type FooRepo interface {
GetAll() ([]Stuff, error)
}
type FooHandler struct {
repo FooRepo
}
func NewFoo(repo FooRepo) *FooHandler {
return &FooHandler{
repo: repo,
}
}
func (f *FooHandler) List(res http.ResponseWriter, req *http.Request) {
all, err := f.repo.GetAll()
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
io.WriteString(w, err.Error())
return
}
// 根据需要编写响应
}
每当部署应用程序的更新版本时,main
函数将调用AutoMigrate
,应用程序将处理请求,而不会不断重新连接到数据库或尝试一次又一次地处理迁移。
我不知道为什么你会认为你的应用程序必须对每个请求运行设置,特别是考虑到你的main函数(或者从main
调用的某个函数)明确地创建了一个HTTP服务器,并在特定端口上监听请求。数据库连接和随后的迁移应该在开始监听请求之前处理。它从来都不是处理请求的一部分...
英文:
It wouldn't happen on every API request. Not even close. It'd happen every time the application is started, so basically: connect to the DB in main
, and run AutoMigrate
there. Pass the connection as a dependency to your handlers/service packages/wherever you need them. The HTTP handler can just access it there.
Basically this:
package main
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
fmt.Printf("Failed to connect to DB: %v", err)
os.Exit(1)
}
// see below how this is handled
fRepo := foo.New(db) // all repos here
fRepo.Migrate() // this handles migrations
// create request handlers
fHandler := handlers.NewFoo(fRepo) // migrations have already been handled
mux := http.NewServeMux()
mux.HandleFunc("/foo/list", fHandler.List) // set up handlers
// start server etc...
}
Have the code that interacts with the DB in some package like this:
package foo
// The DB connection interface as you use it
type Connection interface {
Create()
Find()
AutoMigrate(any)
}
type Foo struct {
db Connection
}
func New(db Connection) *Foo {
return &Foo{
db: db,
}
}
func (f *Foo) Migrate() {
f.db.AutoMigrate(&Stuff{}) // all types this repo deals with go here
}
func (f *Foo) GetAll() ([]Stuff, error) {
ret := []Stuff{}
res := f.db.Find(&ret)
return ret, res.Error
}
Then have your handlers structured in a sensible way, and provide them with the repository (aka foo package stuff):
package handlers
type FooRepo interface {
GetAll() ([]Stuff, error)
}
type FooHandler struct {
repo FooRepo
}
func NewFoo(repo FooRepo) *FooHandler {
return &FooHandler{
repo: repo,
}
}
func (f *FooHandler) List(res http.ResponseWriter, req *http.Request) {
all, err := f.repo.GetAll()
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
io.WriteString(w, err.Error())
return
}
// write response as needed
}
Whenever you deploy an updated version of your application, the main
function will call AutoMigrate
, and the application will handle requests without constantly re-connecting to the DB or attempting to handle migrations time and time again.
I don't know why you'd think that your application would have to run through the setup for each request, especially given that your main function (or some function you call from main
) explicitly creates an HTTP server, and listens on a specific port for requests. The DB connection and subsequent migrations should be handled before you start listening for requests. It's not part of handling requests, ever...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论