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

我需要编写一个应用程序,使用 spring-data 和 mongodb 可以执行复杂的查询。我已经开始使用 MongoRepository,但在寻找示例或实际理解语法方面遇到了复杂的查询问题。

我说的是这样的问题:

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

or the use of JSON based queries which I tried by trial and error because I don't get the syntax right. Even after reading the mongodb documentation (non-working example due to wrong syntax).

@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);
}

在阅读了所有的文档之后,似乎 mongoTemplate的文档比 MongoRepository的文档要好得多。我指的是以下文件:

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

你能告诉我什么使用起来更方便、更强大吗?mongoTemplate还是 MongoRepository?两者都是成熟的吗? 还是其中一个比另一个缺少更多的特征?

91798 次浏览

"Convenient" and "powerful to use" are contradicting goals to some degree. Repositories are by far more convenient than templates but the latter of course give you more fine-grained control over what to execute.

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

DR

我们一般建议采取以下办法:

  1. Start with the repository abstract and just declare simple queries using the query derivation mechanism or manually defined queries.
  2. 对于更复杂的查询,将手动实现的方法添加到存储库中(如本文所述)。

细节

对于你的例子,这看起来像这样:

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

    interface CustomUserRepository {
    
    
    List<User> yourCustomMethod();
    }
    
  2. Add an implementation for this class and follow the naming convention to make sure we can find the class.

    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. Now let your base repository interface extend the custom one and the infrastructure will automatically use your custom implementation:

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

This way you essentially get the choice: everything that just easy to declare goes into UserRepository, everything that's better implemented manually goes into CustomUserRepository. The customization options are documented here.

这个答案可能有点延迟,但我建议避免使用整个存储库路由。你只能得到很少的具有实际价值的实现方法。为了使其正常工作,您可能会遇到 Java 配置这种无聊的东西,您可能会花费数天或数周的时间来研究它,而在文档中没有太多帮助。

相反,使用 MongoTemplate路由并创建您自己的数据访问层,从而将您从 Spring 程序员所面临的配置噩梦中解放出来。由于 MongoTemplate具有很大的灵活性,所以对于那些能够轻松地构建自己的类和交互的工程师来说,MongoTemplate确实是他们的救星。结构可以是这样的:

  1. 创建一个将在应用程序级别运行的 MongoClientFactory类,并为您提供一个 MongoClient对象。您可以将其实现为 Singleton 或使用 Enum Singleton (这是线程安全的)
  2. Create a Data access base class from which you can inherit a data access object for each domain object). The base class can implement a method for creating a MongoTemplate object which you class specific methods can use for all DB accesses
  3. 每个域对象的每个数据访问类可以实现基本方法,也可以在基类中实现它们
  4. 然后,Controller 方法可以根据需要调用 Data 访问类中的方法。

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

  • MongoTemplate 提供 "atomic" out-of-the-box operations updateFirstupdateMultifindAndModifyupsert... 它允许您在单个操作中修改文档。这些方法使用的 Update对象也是 允许您只针对相关字段
  • MongoRepository 只提供 基本的 CRUD 操作 findinsertsavedelete,它们与包含 all the fields的 POJO 一起工作。这迫使您按照几个步骤(1)更新文档。find要更新的文档,2。修改返回的 POJO 中的相关字段,然后3。或者使用 @Query手动定义自己的更新查询。

在一个多线程的环境中,比如一个 Java 后端有几个 REST 端点,单方法更新是可行的,这样可以减少两个并发更新覆盖彼此更改的可能性。

示例: 给定一个像这样的文档: { _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 },因为每个线程只有在指定的字段更改 还有之后才访问 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)
|                                                               |
|                                                               |

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

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