118

我需要编写一个应用程序,我可以使用 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);
} 

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

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

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

4

3 回答 3

157

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

由于存储库编程模型可用于多个 Spring Data 模块,您将在 Spring Data MongoDB参考文档的一般部分中找到更深入的文档。

TL;博士

我们一般推荐以下方法:

  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 {
    
    }
    

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

于 2013-06-09T14:30:36.333 回答
37

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

  • MongoTemplate提供“原子”的开箱即用操作 updateFirst, updateMulti, findAndModify, upsert... 允许您在单个操作中修改文档。Update这些方法使用的对象还允许您仅针对相关字段
  • MongoRepository只为您提供基本的 CRUD 操作 find, insert, save,delete与包含所有字段的 POJO 一起使用。这迫使您分几个步骤更新文档(1.find要更新的文档,2. 从返回的 POJO 修改相关字段,然后 3.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))
|                                                               |
|                                                               |

并且文档的最终状态始终是{ _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)
|                                                               |
|                                                               |

最终的文档是{ _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出于某种原因需要自定义查询功能。

于 2018-04-17T12:34:56.980 回答
30

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

取而代之的是,顺其自然,MongoTemplate创建自己的数据访问层,让您摆脱 Spring 程序员面临的配置噩梦。MongoTemplate对于那些乐于构建自己的类和交互的工程师来说,它确实是救星,因为它具有很大的灵活性。结构可以是这样的:

  1. 创建一个MongoClientFactory将在应用程序级别运行的类并为您提供一个MongoClient对象。您可以将其实现为单例或使用枚举单例(这是线程安全的)
  2. 创建一个数据访问基类,您可以从该基类中为每个域对象继承一个数据访问对象)。基类可以实现用于创建 MongoTemplate 对象的方法,您可以将特定方法用于所有 DB 访问
  3. 每个域对象的每个数据访问类都可以实现基本方法,或者您可以在基类中实现它们
  4. 然后控制器方法可以根据需要调用数据访问类中的方法。
于 2014-12-25T07:36:48.560 回答