我用Swagger创建了一个API,它通过gorm与Mariadb实例通信。我不满意我如何处理db连接模块,并传递db处理程序。我怎样才能使这个更健壮,也许合并接口,并且每次发出db请求时总是调用db.Open
函数。
这是我的配置文件创建由Swagger
func configureAPI(api *operations.HairdooAPI) http.Handler {
// configure the api here
api.ServeError = errors.ServeError
// Set your custom logger if needed. Default one is log.Printf
// Expected interface func(string, ...interface{})
//
// Example:
//api.Logger = log.Printf
api.UseSwaggerUI()
// To continue using redoc as your UI, uncomment the following line
// api.UseRedoc()
api.JSONConsumer = runtime.JSONConsumer()
//api.UrlformConsumer = runtime.DiscardConsumer
api.JSONProducer = runtime.JSONProducer()
api.CompanyAddCompanyHandler = company.AddCompanyHandlerFunc(func(params company.AddCompanyParams) middleware.Responder {
//log.Debugf("Add commpay Handler Called")
//log.Infof("Payload Data : %v", params.Body)
response, err := handlers.AddCompany(params)
if err != nil {
return company.NewAddCompanyBadRequest().WithPayload(&models.ErrorReponse400{
Error: err.Error(),
})
}
return company.NewAddCompanyCreated().WithPayload(response)
})
这是我的AddCompany
函数的实现。
package handlers
import (
"hairdoo.com/m/v2/db"
"hairdoo.com/m/v2/models"
"hairdoo.com/m/v2/restapi/operations/company"
)
func AddCompany(params company.AddCompanyParams) (*models.Company, error) {
result, err := db.AddCompany(params.Body)
if err != nil {
return nil, err
}
return result, nil
}
最后是db包实现
package db
import (
"fmt"
"time"
"gorm.io/gorm"
"hairdoo.com/m/v2/models"
)
type Company struct {
gorm.Model
Name string `gorm:"uniqueIndex"`
Phone string
Email string
ContatctPerson string
CategoryID int
Category *Category
Status string
}
type Category struct {
ID int
Name string `gorm:"uniqueIndex"`
}
func AddCompany(companyObj *models.Company) (*models.Company, error) {
db := Open()
var result *gorm.DB
category := &Category{}
db.Where("name = ?", companyObj.Category.Name).First(category)
company := convertToDBCompany(companyObj, false)
if category.ID != 0 {
result = db.Exec("INSERT INTO companies (created_at, updated_at, name, phone, email, contatct_person, category_id) values (?, ?, ?, ?, ?, ?, ?);",
time.Now(), time.Now(), companyObj.Name, companyObj.Phone, companyObj.Email, companyObj.ContatctPerson, category.ID)
} else {
result = db.Create(company)
}
if result.RowsAffected < 1 {
return nil, result.Error
}
// add employee to db if object present
var employees []*models.EmployeeItems0
if companyObj.Employee != nil {
for _, employeeInput := range companyObj.Employee {
result, err := AddEmployee(employeeInput)
if err != nil {
return nil, err
}
employees = append(employees, result)
}
}
return convertToModelCompany(company, employees), nil
}
这样做的一种方法是创建一个结构体,该结构体存储db连接并将方法放在该结构体上,例如:
type dbService struct {
*gorm.DB
}
func InitDB() *dbService {
db, err := gorm.Open(...)
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&Category{}, &Company{})
return &dbService{db}
}
func (db *dbService) AddCompany(companyObj *models.Company) (*models.Company, error) {
company := convertToDBCompany(companyObj, false)
db.FirstOrInit(company.Category, &Category{Name: companyObj.Category.Name})
result := db.Create(&company)
if result.RowsAffected < 1 {
return nil, result.Error
}
...
}
,如果你已经在使用gorm,我建议你充分利用它的ORM功能;)
您当然不希望每个请求都连接到db,因为Gorm(和底层db库)实现了连接池,以便在多个请求之间透明地重用连接,并且每次重新连接都完全取消了所有辛苦的工作。
总的来说,你需要首先初始化你的应用程序,让它准备好尽快回答请求。
全局使用静态init
一个选项是在包级别静态地进行配置和初始化:
db/db.go
package db
import (
"os"
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
var DB *gorm.DB
func init() {
DB, err = gorm.Open(sqlite.Open(os.Getenv("SQLITE_DB")), &gorm.Config{})
// error handling
}
init
自动运行,此后任何其他包都可以访问db.DB
作为全局对象来运行查询。
全局协调init
对数据库初始化的时间有更多的控制通常更好,因为它可能依赖于必须初始化的值(例如,必须读取配置文件)。在这种情况下,您将把init
重命名为Init
(或其他任何东西),然后从main
或其他设置函数调用该函数,该函数协调应用程序初始化。在go-swagger
中使用标准模板,可以在configureAPI
函数中完成。
func configureAPI(api *operations.HairdooAPI) http.Handler {
// ...
db.Init(...)
// ... setup handlers
}
之后,您应该能够从其他db函数(如AddCompany
)访问打开并准备好的db.DB
全局。
依赖注入样式
以上应该足以回答你的问题;随着你的应用变得越来越大,你可能会考虑一种不同的架构风格,在这种风格中,你不依赖于全局变量,这会使你的代码难以测试。这有点像joelazar
所建议的,每个处理程序或服务都是一个具有*gorm.DB
字段的结构体,该字段在init进程中设置。说明:
package handlers
import "gorm.io/gorm"
type Company struct {
DB *gorm.DB
}
func (h *Company) AddCompany(models.Company, ...) ... {
h.DB.Foo()
}
func configureAPI(api *operations.HairdooAPI) http.Handler {
dbHandler := db.Init(...)
companyHandler := handlers.Company{DB: dbHandler}
...
api.CompanyAddCompanyHandler = company.AddCompanyHandlerFunc(func(params company.AddCompanyParams) middleware.Responder {
response, err := companyHandler.AddCompany(params)
...
}
}
关于这种设置还有很多要说的。使用go-swagger
,我发现平流层模板对于生成耦合更少、更可测试的架构很有用。从长远考虑。