Go 1.18+ 中,可以通过泛型结合 GORM 实现通用数据库操作。以下是一个实现通用 CRUD 方法的示例:
package repository
import (
"gorm.io/gorm"
)
// 定义模型必须实现的接口(例如包含ID字段)
type Model interface {
TableName() string
// 可以添加其他需要实现的约束方法,例如:
// GetID() uint
}
// 通用 Repository 结构体
type GenericRepository[T Model] struct {
DB *gorm.DB
}
func NewRepository[T Model](db *gorm.DB) *GenericRepository[T] {
return &GenericRepository[T]{DB: db}
}
// FindByID 根据ID查询记录
func (r *GenericRepository[T]) FindByID(id uint) (*T, error) {
var entity T
if err := r.DB.First(&entity, id).Error; err != nil {
return nil, err
}
return &entity, nil
}
// Create 创建新记录
func (r *GenericRepository[T]) Create(entity *T) error {
return r.DB.Create(entity).Error
}
// Update 更新记录
func (r *GenericRepository[T]) Update(entity *T) error {
return r.DB.Save(entity).Error
}
// Delete 删除记录
func (r *GenericRepository[T]) Delete(id uint) error {
var entity T
return r.DB.Delete(&entity, id).Error
}
// FindAll 查询所有记录(带分页)
func (r *GenericRepository[T]) FindAll(page, pageSize int) ([]T, int64, error) {
var (
entities []T
count int64
)
db := r.DB.Model(&entities)
db.Count(&count)
if page > 0 && pageSize > 0 {
offset := (page - 1) * pageSize
db = db.Offset(offset).Limit(pageSize)
}
if err := db.Find(&entities).Error; err != nil {
return nil, 0, err
}
return entities, count, nil
}
// Where 条件查询
func (r *GenericRepository[T]) Where(query interface{}, args ...interface{}) *GenericRepository[T] {
r.DB = r.DB.Where(query, args...)
return r
}
// WithPreloads 预加载关联
func (r *GenericRepository[T]) WithPreloads(preloads ...string) *GenericRepository[T] {
for _, preload := range preloads {
r.DB = r.DB.Preload(preload)
}
return r
}
使用示例:
// 定义模型
type User struct {
gorm.Model
Name string
Email string `gorm:"uniqueIndex"`
}
// 实现 Model 接口
func (User) TableName() string {
return "users"
}
func main() {
// 初始化数据库连接
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
// 自动迁移
db.AutoMigrate(&User{})
// 创建 Repository
userRepo := repository.NewRepository[User](db)
// 使用示例
newUser := &User{Name: "John", Email: "[email protected]"}
userRepo.Create(newUser)
// 查询用户
user, _ := userRepo.FindByID(1)
fmt.Println(user)
// 条件查询
users, _, _ := userRepo.Where("name LIKE ?", "%John%").FindAll(1, 10)
}
关键点说明:
- 类型约束:通过
Model
接口确保泛型类型符合数据库模型的基本要求 - 通用操作:封装了常见的 CRUD 操作,减少重复代码
- 链式调用:通过返回 Repository 指针实现链式调用
- 扩展性:可以方便地添加事务支持、软删除处理等
- 类型安全:泛型保证了返回结果的类型正确性
进阶优化方向:
- 添加事务支持
- 处理软删除(DeletedAt)
- 添加缓存层
- 支持更复杂的查询构建
- 增加乐观锁机制
- 添加钩子函数支持
- 支持多数据库方言
注意:使用泛型时要注意 Go 的类型系统的限制,复杂查询可能仍需要特殊处理。对于关联关系的处理,建议结合预加载方法 (Preload) 来实现。