Spring Data的MongoTemplate和MongoRepository有什么区别?

Chr*_*ong 79 java spring mongodb spring-data spring-data-mongodb

我需要编写一个应用程序,我可以使用spring-data和mongodb进行复杂的查询.我一直在使用MongoRepository开始,但在复杂的查询中苦苦寻找示例或实际理解语法.

我在谈论这样的查询:

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    List<User> findByEmailOrLastName(String email, String lastName);
}
Run Code Online (Sandbox Code Playgroud)

或者使用基于JSON的查询,我尝试通过反复试验,因为我没有正确的语法.即使在阅读了mongodb文档(由于语法错误而导致的非工作示例).

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    @Query("'$or':[{'firstName':{'$regex':?0,'$options':'i'}},{'lastName':{'$regex':?0,'$options':'i'}}]")
    List<User> findByEmailOrFirstnameOrLastnameLike(String searchText);
} 
Run Code Online (Sandbox Code Playgroud)

阅读完所有文档后,似乎mongoTemplate记录得更好MongoRepository.我指的是以下文档:

http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/

你能告诉我什么更方便,更强大吗?mongoTemplate还是MongoRepository?两者是否同样成熟,或者其中一个缺少比另一个更多的功能?

Oli*_*ohm 121

"方便"和"强大使用"在某种程度上是相互矛盾的.存储库比模板更方便,但后者当然可以让您对执行的内容进行更细粒度的控制.

由于存储库编程模型可用于多个Spring Data模块,因此您可以在Spring Data MongoDB 参考文档的常规部分中找到更深入的文档.

TL; DR

我们通常建议采用以下方法:

  1. 从存储库摘要开始,只使用查询派生机制或手动定义的查询声明简单查询.
  2. 对于更复杂的查询,请将手动实现的方法添加到存储库(如此处所述).供实施使用MongoTemplate.

细节

对于您的示例,这将看起来像这样:

  1. 为自定义代码定义接口:

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 为此类添加实现并遵循命名约定以确保我们可以找到该类.

    class UserRepositoryImpl implements CustomUserRepository {
    
      private final MongoOperations operations;
    
      @Autowired
      public UserRepositoryImpl(MongoOperations operations) {
    
        Assert.notNull(operations, "MongoOperations must not be null!");
        this.operations = operations;
      }
    
      public List<User> yourCustomMethod() {
        // custom implementation here
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在让您的基础存储库接口扩展自定义存储库接口,基础结构将自动使用您的自定义实现:

    interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

这样你基本上就可以做出选择:易于申报的UserRepository所有内容,手动实现的所有内容都会进入CustomUserRepository.此处记录自定义选项.

  • 最常见的错误是将实现类命名为错误:如果您的基本repo接口名为`YourRepository`,则实现类必须命名为`YourRepositoryImpl`.是这样的吗?如果是这样,我很高兴看一下GitHub上的示例项目...... (8认同)
  • 嗨,奥利弗,因为你假设的Impl课程错误.我调整了名称,现在看起来像是在工作.非常感谢您的反馈.能够以这种方式使用不同类型的查询选项真的很酷.好好想过! (4认同)
  • 嗨,奥利弗,这实际上行不通。spring-data 尝试根据自定义名称自动生成查询。你的自定义方法()。它会说“您的”不是域类中的有效字段。我按照手册进行操作,并仔细检查了 spring-data-jpa-examples 的操作方式。没有运气。当我将自定义接口扩展到存储库类时,spring-data 总是尝试自动生成。唯一的区别是我使用的是 MongoRepository 而不是 CrudRepository,因为我现在不想使用迭代器。如果您有提示,我们将不胜感激。 (2认同)

ram*_*hpa 20

这个答案可能有点延迟,但我建议避免整个存储库路由.你得到的实践方法很少,具有很大的实用价值.为了使它工作,你会遇到Java配置废话,你可以花费数天和数周的时间在文档中没有太多帮助.

相反,请使用该MongoTemplate路线并创建自己的数据访问层,这样您就可以摆脱Spring程序员面临的配置噩梦.MongoTemplate对于那些能够灵活地构建自己的类和交互的工程师来说,它真的是一个救星,因为它具有很大的灵活性.结构可以是这样的:

  1. 创建一个MongoClientFactory将在应用程序级别运行并为您提供MongoClient对象的类.您可以将其实现为Singleton或使用Enum Singleton(这是线程安全的)
  2. 创建一个数据访问基类,您可以从中继承每个域对象的数据访问对象).基类可以实现一种创建MongoTemplate对象的方法,您可以使用该类对特定方法进行所有数据库访问
  3. 每个域对象的每个数据访问类都可以实现基本方法,也可以在基类中实现它们
  4. 然后,Controller方法可以根据需要调用Data访问类中的方法.

  • 您可以,但是您实现的 MongoTemplate 与数据库的连接与存储库使用的连接不同。原子性可能是一个问题。另外,如果您有排序需求,我不建议在一个线程上使用两个不同的连接 (3认同)

wal*_*len 10

FWIW,关于多线程环境中的更新:

  • MongoTemplate提供外的开箱updateFirstupdateMultifindAndModifyupsert...方法,它允许你在一个单一的操作修改文档。Update这些方法使用的对象还允许您仅定位相关字段。
  • MongoRepository只给你基本的findinsertsavedelete运营,包含所有字段的POJO其工作。这迫使您要么分几个步骤find来更新文档(要更新的文档,然后从返回的POJO中修改相关字段,然后再进行修改save),或者使用手动定义自己的更新查询@Query

在多线程环境中(例如,具有多个REST端点的Java后端),单方法更新是行之有效的方法,以减少两次并发更新覆盖彼此更改的机会。

示例:给定这样的文档:{ _id: "ID1", field1: "a string", field2: 10.0 }并且两个不同的线程同时更新它...

有了MongoTemplate它看起来像这样:

THREAD_001                                                      THREAD_002
|                                                               |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
|                                                               |
|                                                               |
Run Code Online (Sandbox Code Playgroud)

文件的最终状态始终是{ _id: "ID1", field1: "another string", field2: 15.0 }因为每个线程仅访问DB一次,并且仅更改了指定字段。

相同的情况MongoRepository如下所示:

THREAD_001                                                      THREAD_002
|                                                               |
|pojo = findById("ID1")                                         |pojo = findById("ID1")
|pojo.setField1("another string") /* field2 still 10.0 */       |pojo.setField2(pojo.getField2()+5) /* field1 still "a string" */
|save(pojo)                                                     |save(pojo)
|                                                               |
|                                                               |
Run Code Online (Sandbox Code Playgroud)

最终文档是{ _id: "ID1", field1: "another string", field2: 10.0 }{ _id: "ID1", field1: "a string", field2: 15.0 }取决于哪个save操作首先命中数据库。

因此,我想这MongoTemplate是一个更好的选择,除非您有一个非常详细的POJO模型或MongoRepository出于某种原因需要自定义查询功能。

  • @Madbreaks 感谢您指出这一点。是的,“@Version”将“避免”第二个线程覆盖第一个线程保存的数据——“避免”是指它将放弃更新并抛出“OptimisticLockingFailureException”。因此,如果您希望更新成功,则必须实施重试机制。MongoTemplate 可以让您避免整个场景。 (2认同)