MongoDB订单匹配阻塞列表查询实现

小豆丁 4小时前 ⋅ 5 阅读
聊天记录
头像

public interface OrderMatchBlockListDocumentRepository extends OrderMatchBlockListDocumentRepositoryExtension, MongoRepository<OrderMatchBlockListDocument, Long> { Optional<OrderMatchBlockListDocument> findFirstByPayOrderIdOrderByCreatedAtDesc(Long payOrderId); }

@Component public class OrderMatchBlockListDocumentRepositoryImpl extends AbstractRepoExtension implements OrderMatchBlockListDocumentRepositoryExtension { public OrderMatchBlockListDocumentRepositoryImpl(@Qualifier(MatchPushMongo.MONGO_TEMPLATE) MongoOperations mongoOperations) { super(mongoOperations); }

//参考:com.qibingdaojia.turnright.datasource.mongo.matchpush.repository.OrderMatchBlockListDocumentRepositoryImpl#findPayOrderIdsByLocksmithId
@Override
public List&lt;Long&gt; findPayOrderIdsByLocksmithId(Long locksmithId) {
    Criteria criteria = Criteria
            .where(&quot;blockLocksmithIdList&quot;).is(locksmithId)
            .and(&quot;deletedAt&quot;).is(null);

    Query query = Query.query(criteria);
    query.fields()
            .include(&quot;payOrderId&quot;)
            .exclude(&quot;_id&quot;);

    return mongoOperations.find(query, OrderMatchBlockListDocument.class)
            .stream()
            .map(OrderMatchBlockListDocument::getPayOrderId)
            .collect(Collectors.toList());
}

} public class OrderMatchBlockListDocumentRepositoryImpl extends AbstractRepoExtension implements OrderMatchBlockListDocumentRepositoryExtension 这样写有没有问题

头像
头像

public interface OrderMatchBlockListDocumentRepository extends OrderMatchBlockListDocumentRepositoryExtension, MongoRepository<OrderMatchBlockListDocument, Long> { Optional<OrderMatchBlockListDocument> findFirstByPayOrderIdOrderByCreatedAtDesc(Long payOrderId); } public interface OrderMatchBlockListDocumentRepositoryExtension {

List&lt;Long&gt; findPayOrderIdsByLocksmithId(Long locksmithId);

} @Component public class OrderMatchBlockListDocumentRepositoryImpl extends AbstractRepoExtension implements OrderMatchBlockListDocumentRepositoryExtension { public OrderMatchBlockListDocumentRepositoryImpl(@Qualifier(MatchPushMongo.MONGO_TEMPLATE) MongoOperations mongoOperations) { super(mongoOperations); }

//参考:com.qibingdaojia.turnright.datasource.mongo.matchpush.repository.OrderMatchBlockListDocumentRepositoryImpl#findPayOrderIdsByLocksmithId
@Override
public List&lt;Long&gt; findPayOrderIdsByLocksmithId(Long locksmithId) {
    Criteria criteria = Criteria
            .where(&quot;blockLocksmithIdList&quot;).is(locksmithId)
            .and(&quot;deletedAt&quot;).is(null);

    Query query = Query.query(criteria);
    query.fields()
            .include(&quot;payOrderId&quot;)
            .exclude(&quot;_id&quot;);

    return mongoOperations.find(query, OrderMatchBlockListDocument.class)
            .stream()
            .map(OrderMatchBlockListDocument::getPayOrderId)
            .collect(Collectors.toList());
}

} OrderMatchBlockListDocumentRepository extends OrderMatchBlockListDocumentRepositoryExtension 这样写有没有问题

头像

这样写完全没有问题。这种结构是 Spring Data 的推荐做法,被称为“自定义 Repository 扩展”。

让我们分解一下这个结构:

  1. OrderMatchBlockListDocumentRepository (主接口)

    • 继承了 MongoRepository:这为你提供了所有Spring Data内置的标准CRUD和查询方法(如save, findById, findAll等)。
    • 继承了 OrderMatchBlockListDocumentRepositoryExtension (你的自定义扩展接口):这声明了你需要但Spring Data无法自动生成的自定义、复杂查询方法。
  2. OrderMatchBlockListDocumentRepositoryExtension (自定义扩展接口)

    • 定义了一个自定义方法 findPayOrderIdsByLocksmithId。Spring Data无法仅通过方法名自动实现这个相对复杂的查询。
  3. OrderMatchBlockListDocumentRepositoryImpl (自定义扩展实现类)

    • 实现了 OrderMatchBlockListDocumentRepositoryExtension 接口。
    • 使用 MongoOperations 手动编写具体的查询逻辑来满足 findPayOrderIdsByLocksmithId 方法的需求。

Spring Data 的魔法在于:当你的服务层注入 OrderMatchBlockListDocumentRepository 时,Spring Data 会自动将两部分实现合并成一个可用的 Bean。

  • 来自 MongoRepository 的标准方法由 Spring Data 提供实现。
  • 来自 OrderMatchBlockListDocumentRepositoryExtension 的自定义方法,其实现由你编写的 OrderMatchBlockListDocumentRepositoryImpl 提供。

因此,你可以像使用一个普通的 Repository 一样使用它:

JAVA
@Service
public class MyService {
    @Autowired
    private OrderMatchBlockListDocumentRepository repository; // 注入合并后的Repository

    public void doSomething() {
        // 1. 使用标准方法 (来自 MongoRepository)
        Optional<OrderMatchBlockListDocument> doc = repository.findFirstByPayOrderIdOrderByCreatedAtDesc(123L);

        // 2. 使用自定义方法 (来自 OrderMatchBlockListDocumentRepositoryExtension, 实现由 Impl 类提供)
        List<Long> orderIds = repository.findPayOrderIdsByLocksmithId(456L);
    }
}

总结: 你提供的代码结构是标准、正确且优雅的。它完美地结合了 Spring Data 的声明式编程的便利性和手动编写复杂查询的灵活性。这是一种常见的、官方推荐的最佳实践。

头像

Spring Data 会自动将两部分实现合并成一个可用的 Bean 什么原理,OrderMatchBlockListDocumentRepositoryExtension还可以用吗?

头像

你的理解完全正确,让我详细解释一下这个机制的原理:

合并原理:Spring Data 的 "组合代理" 模式

Spring Data 在启动时会为每个 Repository 接口创建动态代理,这个代理会组合多个实现来源

  1. Spring Data 的标准实现 - 由框架生成
  2. 你的自定义实现 - 从 *Impl 类获取
  3. 查询方法实现 - 基于方法名解析自动生成

具体工作流程:

JAVA
// 1. 启动时,Spring Data 发现这个接口
public interface OrderMatchBlockListDocumentRepository 
    extends OrderMatchBlockListDocumentRepositoryExtension,  // ← 扩展接口
            MongoRepository<OrderMatchBlockListDocument, Long> {  // ← 基础接口
    // 声明式查询方法
    Optional<OrderMatchBlockListDocument> findFirstByPayOrderIdOrderByCreatedAtDesc(Long payOrderId);
}

// 2. Spring Data 创建代理,组合实现:
代理类 OrderMatchBlockListDocumentRepositoryProxy {
    // 包含三个处理器:
    - 基础处理器:处理 save(), findById(), delete() 等标准方法
    - 查询处理器:处理 findFirstByPayOrderIdOrderByCreatedAtDesc 等声明式查询
    - 自定义处理器:处理 findPayOrderIdsByLocksmithId 等自定义方法
}

关于 OrderMatchBlockListDocumentRepositoryExtension 的可用性

可以单独使用,但不推荐,有更好的使用方式:

方式1:直接使用(可行但不优雅)

JAVA
@Service
public class MyService {
    // 可以单独注入扩展接口
    @Autowired
    private OrderMatchBlockListDocumentRepositoryExtension repositoryExtension;
    
    public void doSomething() {
        List<Long> ids = repositoryExtension.findPayOrderIdsByLocksmithId(123L);
        // 但这里无法使用标准CRUD方法
    }
}

方式2:推荐用法 - 通过主接口使用

JAVA
@Service
public class MyService {
    // 推荐:注入完整的主接口
    @Autowired
    private OrderMatchBlockListDocumentRepository repository;
    
    public void doSomething(Long locksmithId) {
        // 1. 使用自定义方法
        List<Long> orderIds = repository.findPayOrderIdsByLocksmithId(locksmithId);
        
        // 2. 使用标准方法
        repository.save(newDocument);
        
        // 3. 使用声明式查询
        Optional<OrderMatchBlockListDocument> doc = 
            repository.findFirstByPayOrderIdOrderByCreatedAtDesc(123L);
    }
}

方式3:扩展接口的复用(高级用法)

JAVA
// 定义通用扩展接口
public interface CustomBlockListRepository {
    List<Long> findPayOrderIdsByLocksmithId(Long locksmithId);
    // 其他通用方法...
}

// 多个Repository可以复用
public interface OrderMatchBlockListDocumentRepository 
    extends CustomBlockListRepository, MongoRepository<OrderMatchBlockListDocument, Long> {
    // ...
}

public interface OtherBlockListRepository 
    extends CustomBlockListRepository, MongoRepository<OtherDocument, Long> {
    // ...
}

技术实现细节

Spring Data 通过以下步骤实现合并:

JAVA
// Spring Data内部简化流程:
1. 扫描所有继承 Repository 的接口
2. 为每个接口创建 RepositoryFactoryBean
3. 在 RepositoryFactoryBean 中:
   a. 查找是否有对应的 *Impl 类
   b. 如果有,将其方法添加到代理的方法列表中
   c. 将声明式查询方法也添加到代理
   d. 将所有方法统一路由到正确的处理器
4. 注册一个合并后的代理Bean到Spring容器

总结建议

  1. 直接使用主接口OrderMatchBlockListDocumentRepository
  2. 不要单独使用扩展接口:除非有特殊的多态需求
  3. 扩展接口的真正价值:是定义契约,而不是被直接注入
  4. 实现类的作用:是实现扩展接口的契约,供Spring Data合并使用

你的代码结构是正确的,这种设计模式让你既能享受Spring Data的便利,又能灵活实现复杂查询,是最佳实践。

全部评论: 0

    我有话说: