372 lines
11 KiB
Markdown
372 lines
11 KiB
Markdown
# Repository 迁移指南
|
||
|
||
## 从传统模式迁移到 BaseRepositoryImpl 模式
|
||
|
||
### 步骤 1:修改结构体定义
|
||
|
||
**之前:**
|
||
```go
|
||
type GormExampleRepository struct {
|
||
db *gorm.DB
|
||
logger *zap.Logger
|
||
}
|
||
```
|
||
|
||
**之后:**
|
||
```go
|
||
type GormExampleRepository struct {
|
||
*database.BaseRepositoryImpl // 嵌入基础Repository实现
|
||
}
|
||
```
|
||
|
||
### 步骤 2:修改构造函数
|
||
|
||
**之前:**
|
||
```go
|
||
func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
|
||
return &GormExampleRepository{
|
||
db: db,
|
||
logger: logger,
|
||
}
|
||
}
|
||
```
|
||
|
||
**之后:**
|
||
```go
|
||
func NewGormExampleRepository(db *gorm.DB, logger *zap.Logger) ExampleRepository {
|
||
return &GormExampleRepository{
|
||
BaseRepositoryImpl: database.NewBaseRepositoryImpl(db, logger),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 步骤 3:删除 getDB 方法
|
||
|
||
**删除这样的代码:**
|
||
```go
|
||
func (r *GormExampleRepository) getDB(ctx context.Context) *gorm.DB {
|
||
if tx, ok := database.GetTx(ctx); ok {
|
||
return tx
|
||
}
|
||
return r.db
|
||
}
|
||
```
|
||
|
||
### 步骤 4:实现 Repository[T] 接口方法
|
||
|
||
#### 基础 CRUD 操作
|
||
|
||
**Create - 之前:**
|
||
```go
|
||
func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) {
|
||
r.logger.Info("创建实体", zap.String("id", entity.ID))
|
||
err := r.getDB(ctx).WithContext(ctx).Create(&entity).Error
|
||
return entity, err
|
||
}
|
||
```
|
||
|
||
**Create - 之后:**
|
||
```go
|
||
func (r *GormExampleRepository) Create(ctx context.Context, entity Entity) (Entity, error) {
|
||
r.GetLogger().Info("创建实体", zap.String("id", entity.ID))
|
||
err := r.BaseRepositoryImpl.Create(ctx, &entity)
|
||
return entity, err
|
||
}
|
||
```
|
||
|
||
**GetByID - 之前:**
|
||
```go
|
||
func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) {
|
||
var entity Entity
|
||
err := r.getDB(ctx).WithContext(ctx).Where("id = ?", id).First(&entity).Error
|
||
return entity, err
|
||
}
|
||
```
|
||
|
||
**GetByID - 之后:**
|
||
```go
|
||
func (r *GormExampleRepository) GetByID(ctx context.Context, id string) (Entity, error) {
|
||
var entity Entity
|
||
err := r.BaseRepositoryImpl.GetByID(ctx, id, &entity)
|
||
return entity, err
|
||
}
|
||
```
|
||
|
||
**Update - 之前:**
|
||
```go
|
||
func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error {
|
||
r.logger.Info("更新实体", zap.String("id", entity.ID))
|
||
return r.getDB(ctx).WithContext(ctx).Save(&entity).Error
|
||
}
|
||
```
|
||
|
||
**Update - 之后:**
|
||
```go
|
||
func (r *GormExampleRepository) Update(ctx context.Context, entity Entity) error {
|
||
r.GetLogger().Info("更新实体", zap.String("id", entity.ID))
|
||
return r.BaseRepositoryImpl.Update(ctx, &entity)
|
||
}
|
||
```
|
||
|
||
#### 批量操作
|
||
|
||
**CreateBatch:**
|
||
```go
|
||
func (r *GormExampleRepository) CreateBatch(ctx context.Context, entities []Entity) error {
|
||
r.GetLogger().Info("批量创建实体", zap.Int("count", len(entities)))
|
||
return r.BaseRepositoryImpl.CreateBatch(ctx, &entities)
|
||
}
|
||
```
|
||
|
||
**GetByIDs:**
|
||
```go
|
||
func (r *GormExampleRepository) GetByIDs(ctx context.Context, ids []string) ([]Entity, error) {
|
||
var entities []Entity
|
||
err := r.BaseRepositoryImpl.GetByIDs(ctx, ids, &entities)
|
||
return entities, err
|
||
}
|
||
```
|
||
|
||
**UpdateBatch:**
|
||
```go
|
||
func (r *GormExampleRepository) UpdateBatch(ctx context.Context, entities []Entity) error {
|
||
r.GetLogger().Info("批量更新实体", zap.Int("count", len(entities)))
|
||
return r.BaseRepositoryImpl.UpdateBatch(ctx, &entities)
|
||
}
|
||
```
|
||
|
||
**DeleteBatch:**
|
||
```go
|
||
func (r *GormExampleRepository) DeleteBatch(ctx context.Context, ids []string) error {
|
||
r.GetLogger().Info("批量删除实体", zap.Strings("ids", ids))
|
||
return r.BaseRepositoryImpl.DeleteBatch(ctx, ids, &Entity{})
|
||
}
|
||
```
|
||
|
||
### 步骤 5:实现 BaseRepository 接口方法
|
||
|
||
#### 基础操作
|
||
|
||
**Delete:**
|
||
```go
|
||
func (r *GormExampleRepository) Delete(ctx context.Context, id string) error {
|
||
r.GetLogger().Info("删除实体", zap.String("id", id))
|
||
return r.BaseRepositoryImpl.Delete(ctx, id, &Entity{})
|
||
}
|
||
```
|
||
|
||
**Exists:**
|
||
```go
|
||
func (r *GormExampleRepository) Exists(ctx context.Context, id string) (bool, error) {
|
||
return r.BaseRepositoryImpl.Exists(ctx, id, &Entity{})
|
||
}
|
||
```
|
||
|
||
**Count:**
|
||
```go
|
||
func (r *GormExampleRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
|
||
// 如果需要自定义搜索逻辑
|
||
if options.Search != "" {
|
||
return r.CountWhere(ctx, &Entity{}, "name LIKE ? OR description LIKE ?",
|
||
"%"+options.Search+"%", "%"+options.Search+"%")
|
||
}
|
||
return r.BaseRepositoryImpl.Count(ctx, &Entity{}, options)
|
||
}
|
||
```
|
||
|
||
**SoftDelete:**
|
||
```go
|
||
func (r *GormExampleRepository) SoftDelete(ctx context.Context, id string) error {
|
||
r.GetLogger().Info("软删除实体", zap.String("id", id))
|
||
return r.BaseRepositoryImpl.SoftDelete(ctx, id, &Entity{})
|
||
}
|
||
```
|
||
|
||
**Restore:**
|
||
```go
|
||
func (r *GormExampleRepository) Restore(ctx context.Context, id string) error {
|
||
r.GetLogger().Info("恢复实体", zap.String("id", id))
|
||
return r.BaseRepositoryImpl.Restore(ctx, id, &Entity{})
|
||
}
|
||
```
|
||
|
||
#### 列表查询
|
||
|
||
**List:**
|
||
```go
|
||
func (r *GormExampleRepository) List(ctx context.Context, options interfaces.ListOptions) ([]Entity, error) {
|
||
var entities []Entity
|
||
|
||
// 如果需要自定义搜索逻辑
|
||
if options.Search != "" {
|
||
query := r.GetDB(ctx).Model(&Entity{})
|
||
|
||
// 应用筛选条件
|
||
if options.Filters != nil {
|
||
for key, value := range options.Filters {
|
||
query = query.Where(key+" = ?", value)
|
||
}
|
||
}
|
||
|
||
// 自定义搜索逻辑
|
||
query = query.Where("name LIKE ? OR description LIKE ?",
|
||
"%"+options.Search+"%", "%"+options.Search+"%")
|
||
|
||
// 应用预加载
|
||
for _, include := range options.Include {
|
||
query = query.Preload(include)
|
||
}
|
||
|
||
// 应用排序
|
||
if options.Sort != "" {
|
||
order := "ASC"
|
||
if options.Order == "desc" || options.Order == "DESC" {
|
||
order = "DESC"
|
||
}
|
||
query = query.Order(options.Sort + " " + order)
|
||
} else {
|
||
query = query.Order("created_at DESC")
|
||
}
|
||
|
||
// 应用分页
|
||
if options.Page > 0 && options.PageSize > 0 {
|
||
offset := (options.Page - 1) * options.PageSize
|
||
query = query.Offset(offset).Limit(options.PageSize)
|
||
}
|
||
|
||
return entities, query.Find(&entities).Error
|
||
}
|
||
|
||
// 使用基础实现
|
||
err := r.BaseRepositoryImpl.List(ctx, &Entity{}, &entities, options)
|
||
return entities, err
|
||
}
|
||
```
|
||
|
||
### 步骤 6:业务方法使用辅助方法
|
||
|
||
**使用 FindOne:**
|
||
```go
|
||
func (r *GormExampleRepository) GetByCode(ctx context.Context, code string) (*Entity, error) {
|
||
var entity Entity
|
||
err := r.FindOne(ctx, &entity, "code = ?", code)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &entity, nil
|
||
}
|
||
```
|
||
|
||
**使用 FindWhere:**
|
||
```go
|
||
func (r *GormExampleRepository) GetByStatus(ctx context.Context, status string) ([]Entity, error) {
|
||
var entities []Entity
|
||
err := r.FindWhere(ctx, &entities, "status = ?", status)
|
||
return entities, err
|
||
}
|
||
```
|
||
|
||
**使用 CountWhere:**
|
||
```go
|
||
func (r *GormExampleRepository) CountByStatus(ctx context.Context, status string) (int64, error) {
|
||
return r.CountWhere(ctx, &Entity{}, "status = ?", status)
|
||
}
|
||
```
|
||
|
||
**使用 ExistsWhere:**
|
||
```go
|
||
func (r *GormExampleRepository) ExistsByCode(ctx context.Context, code string) (bool, error) {
|
||
return r.ExistsWhere(ctx, &Entity{}, "code = ?", code)
|
||
}
|
||
```
|
||
|
||
### 步骤 7:复杂查询仍使用 GetDB
|
||
|
||
对于复杂查询,继续使用 `r.GetDB(ctx)` 来获取数据库连接:
|
||
|
||
```go
|
||
func (r *GormExampleRepository) ComplexQuery(ctx context.Context, params QueryParams) ([]Entity, error) {
|
||
var entities []Entity
|
||
query := r.GetDB(ctx).Model(&Entity{})
|
||
|
||
// 添加复杂的查询逻辑
|
||
if params.Status != "" {
|
||
query = query.Where("status = ?", params.Status)
|
||
}
|
||
|
||
if params.DateRange != nil {
|
||
query = query.Where("created_at BETWEEN ? AND ?", params.DateRange.Start, params.DateRange.End)
|
||
}
|
||
|
||
// 连接查询
|
||
query = query.Joins("LEFT JOIN related_table ON entities.related_id = related_table.id").
|
||
Where("related_table.active = ?", true)
|
||
|
||
return entities, query.Find(&entities).Error
|
||
}
|
||
```
|
||
|
||
## BaseRepositoryImpl 提供的方法
|
||
|
||
### 核心方法
|
||
- `GetDB(ctx)` - 获取数据库连接(自动支持事务)
|
||
- `GetLogger()` - 获取日志记录器
|
||
- `WithTx(tx)` - 创建事务版本的Repository
|
||
|
||
### 基础 CRUD
|
||
- `Create(ctx, entity)` - 创建实体
|
||
- `GetByID(ctx, id, entity)` - 根据ID获取
|
||
- `Update(ctx, entity)` - 更新实体
|
||
- `Delete(ctx, id, entity)` - 删除实体
|
||
- `Exists(ctx, id, entity)` - 检查存在
|
||
|
||
### 批量操作
|
||
- `CreateBatch(ctx, entities)` - 批量创建
|
||
- `GetByIDs(ctx, ids, entities)` - 批量获取
|
||
- `UpdateBatch(ctx, entities)` - 批量更新
|
||
- `DeleteBatch(ctx, ids, entity)` - 批量删除
|
||
|
||
### 查询方法
|
||
- `List(ctx, entity, entities, options)` - 列表查询
|
||
- `Count(ctx, entity, options)` - 计数查询
|
||
- `FindWhere(ctx, entities, condition, args...)` - 条件查询
|
||
- `FindOne(ctx, entity, condition, args...)` - 单个查询
|
||
- `CountWhere(ctx, entity, condition, args...)` - 条件计数
|
||
- `ExistsWhere(ctx, entity, condition, args...)` - 条件存在
|
||
|
||
### 软删除
|
||
- `SoftDelete(ctx, id, entity)` - 软删除
|
||
- `Restore(ctx, id, entity)` - 恢复
|
||
|
||
### 事务辅助
|
||
- `ExecuteInTransaction(ctx, fn)` - 执行事务
|
||
- `IsInTransaction(ctx)` - 检查事务状态
|
||
|
||
## 优势
|
||
|
||
1. **统一事务处理**:所有Repository自动支持事务
|
||
2. **减少代码重复**:移除重复的getDB方法和基础CRUD实现
|
||
3. **提高可维护性**:统一的事务逻辑在一个地方维护
|
||
4. **类型安全**:编译时检查,减少运行时错误
|
||
5. **更清晰的职责**:Repository专注于业务逻辑,基础功能由BaseRepository提供
|
||
6. **完整的接口支持**:自动实现Repository[T]和BaseRepository的所有方法
|
||
|
||
## 迁移检查清单
|
||
|
||
- [ ] 修改结构体定义,嵌入 BaseRepositoryImpl
|
||
- [ ] 更新构造函数
|
||
- [ ] 删除 getDB 方法
|
||
- [ ] 实现 Repository[T] 接口的所有方法
|
||
- [ ] 实现 BaseRepository 接口的所有方法
|
||
- [ ] 使用辅助方法替换重复的查询逻辑
|
||
- [ ] 为特殊需求重写搜索逻辑
|
||
- [ ] 运行测试确保功能正常
|
||
- [ ] 更新相关文档
|
||
|
||
## 注意事项
|
||
|
||
1. **方法参数**:BaseRepositoryImpl的方法使用`interface{}`,需要传递指针
|
||
2. **搜索逻辑**:默认搜索逻辑可能不适合所有实体,需要重写
|
||
3. **预加载**:使用ListOptions的Include字段或直接调用GetDB()
|
||
4. **事务支持**:所有方法自动支持事务,无需额外处理
|
||
5. **日志记录**:使用GetLogger()而不是直接访问logger字段 |