I prepared GORM methods in Database.go and I want to keep them generic. The reason I do this is to avoid duplicate codes. I have prepared frequently used operations as below.
package database
import (
"context"
"fmt"
"go-microservice/internal/constants"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"time"
)
type DbOperations[T any] interface {
Ready() bool
GetAll(ctx context.Context) ([]T, error)
GetByParam(ctx context.Context, param any) (any, error)
Create(ctx context.Context, data any) error
Update(ctx context.Context, data any) error
Delete(ctx context.Context, model any, param any) error
}
type DbConnection[T any] struct {
DB *gorm.DB
}
func NewDatabaseConnection[T any]() DbConnection[T] {
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s",
constants.Host,
constants.User,
constants.Password,
constants.Dbname,
constants.Port,
constants.SSLMode)
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "wisdom.",
},
NowFunc: func() time.Time {
return time.Now().UTC()
},
QueryFields: true})
return DbConnection[T]{DB: db}
}
func (db *DbConnection[T]) Ready() bool {
var ready string
tx := db.DB.Raw("SELECT 1 as ready").Scan(&ready)
if tx.Error != nil {
return false
}
if ready == "1" {
return true
}
return false
}
func (db *DbConnection[T]) GetAll(ctx context.Context) ([]T, error) {
list := make([]T, 0)
result := db.DB.WithContext(ctx).Find(&list)
return list, result.Error
}
func (db *DbConnection[T]) GetByParam(ctx context.Context, param any) (any, error) {
var model any
result := db.DB.WithContext(ctx).Where(param).Find(&model)
return model, result.Error
}
func (db *DbConnection[T]) Create(ctx context.Context, data any) error {
result := db.DB.WithContext(ctx).Create(data)
return result.Error
}
func (db *DbConnection[T]) Update(ctx context.Context, data any) error {
result := db.DB.WithContext(ctx).Model(data)
return result.Error
}
func (db *DbConnection[T]) Delete(ctx context.Context, model any, param any) error {
result := db.DB.WithContext(ctx).Delete(model, param)
return result.Error
}
When I want to use these methods, for example db_products, I want to pull the method and use it, but when I do this I get an error. db_products.go codes are as follows:
package database
import (
"context"
"go-microservice/internal/models"
)
type ProductOperations interface {
GetAllProducts(ctx context.Context) ([]models.Products, error)
}
func (db *DbConnection[T]) GetAllProducts(ctx context.Context) ([]models.Products, error) {
var re []models.Products
re, _ = db.GetAll(ctx)
return re, nil
}
But, here I am getting error from IDE. The error is as follows:
Cannot assign []T to re (type []models.Products) in multiple assignment
How do I solve this error? Thank you.
Summary
There is a difference between
anyin type parameter andanyin regular function parameter. Your case is the former one for generics. So you need to instantiateT(decideT's concrete type) first when assigning it to the concrete variablesIn detail
Type definition spec
Instantiations spec
It's because in
GetAllProducts, typeTis not decided yet. So it is impossible to assign[]models.ProductstoT.But you can assign
models.ProductstoTifTis decided asmodels.Products, like this.I reproduce your problem in the simplified version(https://go.dev/play/p/GHkDThFLOKG). You can find the same error
GetAllProducts()but in the main code, it works.References
Go error: cannot use generic type without instantiation
Difference between any/interface{} as constraint vs. type of argument?