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

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

我说的是这样的查询:

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

或者使用基于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);
} 

在通读了所有文档之后,似乎记录得更好。我指的是以下文档:mongoTemplateMongoRepository

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

你能告诉我什么更方便,更强大的使用吗? 或?两者是否都同样成熟,或者其中一个比另一个缺乏更多的功能?mongoTemplateMongoRepository


答案 1

“方便”和“使用功能强大”在某种程度上是相互矛盾的目标。存储库比模板方便得多,但后者当然可以让您更精细地控制要执行的内容。

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

TL;DR

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

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

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

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

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
    
  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
      }
    }
    
  3. 现在,让您的基本存储库接口扩展自定义存储库接口,基础架构将自动使用您的自定义实现:

    interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
    
    }
    

通过这种方式,您基本上可以选择:所有容易声明的内容都进入,所有更好地手动实现的内容都进入。此处记录了自定义选项。UserRepositoryCustomUserRepository


答案 2

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

  • MongoTemplate提供“原子”开箱即用的操作 , , , ...它允许您在单个操作中修改文档。这些方法使用的对象还允许您仅以相关字段为目标updateFirstupdateMultifindAndModifyupsertUpdate
  • MongoRepository只给你基本的CRUD操作,,,,它们适用于包含所有字段的POJO。这迫使您通过几个步骤更新文档 (1. 要更新的文档, 2.修改返回的 POJO 中的相关字段,然后修改 3。 it),或使用 手动定义您自己的更新查询。findinsertsavedeletefindsave@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))
|                                                               |
|                                                               |

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

而 相同的情况场景将如下所示: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)
|                                                               |
|                                                               |

并且最终文档是或取决于哪个操作最后到达数据库。
(注意:即使我们按照注释中的建议使用了Spring Data的@Version注释,也不会有太大变化:其中一个保存操作会抛出一个ConvedLockingFailureException,最终文档仍然是上述内容之一,只有一个字段更新而不是两个字段。{ _id: "ID1", field1: "another string", field2: 10.0 }{ _id: "ID1", field1: "a string", field2: 15.0 }save

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