I need to write an application with which I can do complex queries using spring-data and mongodb. I have been starting by using the MongoRepository but struggled with complex queries to find examples or to actually understand the Syntax.
我需要编写一个应用程序,可以使用spring-data和mongodb执行复杂的查询。我从使用MongoRepository开始,但在寻找示例或真正理解语法的复杂查询时遇到了困难。
I'm talking about queries like this:
我指的是这样的查询:
@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).
或者使用基于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);
}
After reading through all the documentation it seems that mongoTemplate
is far better documented then MongoRepository
. I'm referring to following documentation:
在阅读了所有的文档之后,mongoTemplate比MongoRepository要好得多。我指的是以下文件:
http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/
http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/
Can you tell me what is more convenient and powerful to use? mongoTemplate
or MongoRepository
? Are both same mature or does one of them lack more features then the other?
你能告诉我用什么更方便、更强大吗?mongoTemplate还是MongoRepository ?两者都是成熟的还是一个比另一个缺少更多的特性?
3 个解决方案
#1
101
"Convenient" and "powerful to use" are contradicting goals to some degree. Repositories are by far more convenient that the templates but the latter of course give you more fine-grained control over what to execute.
“方便”和“强大的使用”在某种程度上与目标相矛盾。到目前为止,存储库比模板要方便得多,但后者当然会为您提供对执行内容的更细粒度控制。
As the repository programming model is available for multiple Spring Data modules you find more in-depth documentation of it in the general section of the Spring Data MongoDB reference docs.
由于存储库编程模型可用于多个Spring数据模块,因此您可以在Spring Data MongoDB参考文档的一般部分找到更深入的文档。
TL;DR
博士TL;
We generally recommend the following approach:
我们一般建议采用以下方法:
- Start with the repository abstract and just declare simple queries using the query derivation mechanism or manually defined queries.
- 从存储库抽象开始,使用查询派生机制或手动定义查询声明简单查询。
- For more complex queries, add manually implemented methods to the repository (as documented here). For the implementation use
MongoTemplate
. - 对于更复杂的查询,请向存储库中添加手动实现的方法(如本文所述)。对于实现使用MongoTemplate。
Details
细节
For your example this would look something like this:
在你的例子中,这个看起来是这样的:
-
Define an interface for your custom code:
为您的自定义代码定义一个接口:
interface CustomUserRepository { List<User> yourCustomMethod(); }
-
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 } }
-
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.
通过这种方式,您基本上可以做出选择:所有容易声明的内容都进入UserRepository,所有更好地手动实现的内容都进入CustomUserRepository。这里记录了定制选项。
#2
11
This answer may be a bit delayed, but I would recommend avoiding the whole repository route. You get very little implemented methods of any great practical value. In order to make it work you run into the Java configuration nonsense which you can spend days and weeks on without much help in the documentation.
这个答案可能有点延迟,但是我建议避免使用整个存储库路径。你得到的实现方法很少有任何实际价值。为了使它正常工作,您会遇到Java配置无意义的问题,您可以在文档中不需要太多帮助的情况下花费数天或数周的时间。
Instead, go with the MongoTemplate
route and create your own Data access layer which frees you from the configuration nightmares faced by Spring programmers. MongoTemplate
is really the savior for engineers who are comfortable architecting their own classes and interactions since there is lot of flexibility. The structure can be something like this:
相反,使用MongoTemplate路由创建您自己的数据访问层,从而将您从Spring程序员面临的配置噩梦中解放出来。MongoTemplate是那些轻松构建自己的类和交互的工程师的救星,因为它具有很大的灵活性。结构可以是这样的:
- Create a
MongoClientFactory
class that will run at the application level and give you aMongoClient
object. You can implement this as a Singleton or using an Enum Singleton (this is thread safe) - 创建将在应用程序级别上运行的MongoClientFactory类,并为您提供MongoClient对象。您可以将其实现为单例或使用Enum单例(这是线程安全的)
- 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
- 创建一个数据访问基类,从中可以继承每个域对象的数据访问对象)。基类可以实现创建MongoTemplate对象的方法,您可以将特定的方法用于所有的DB访问
- Each data access class for each domain object can implement the basic methods or you can implement them in the base class
- 每个域对象的每个数据访问类都可以实现基本方法,或者您可以在基类中实现它们。
- The Controller methods can then call methods in the Data access classes as needed.
- 然后,控制器方法可以根据需要调用数据访问类中的方法。
#3
0
FWIW, regarding updates in a multi-threaded environment:
FWIW,关于多线程环境中的更新:
-
MongoTemplate
provides out-of-the-boxupdateFirst
,updateMulti
,findAndModify
,upsert
... methods which allow you to modify a document in a single operation. TheUpdate
object used by these methods also allows you to target only the relevant fields. - MongoTemplate提供开箱即用的updateFirst、updateMulti、findAndModify、upsert…方法,允许您在单个操作中修改文档。这些方法使用的Update对象还允许您只针对相关字段。
-
MongoRepository
only gives you the basicfind
,insert
,save
,delete
operations, which work with POJOs containing all the fields. This forces you to either update the documents in several steps (find
the document to update, modify the relevant fields from the returned POJO, and thensave
it), or define your own update queries by hand using@Query
. - MongoRepository只提供基本的查找、插入、保存、删除操作,这些操作与包含所有字段的pojo一起使用。这将迫使您在几个步骤中更新文档(找到要更新的文档,从返回的POJO中修改相关字段,然后保存它),或者使用@Query手工定义您自己的更新查询。
In a multi-threaded environment, like e.g. a Java back-end with several REST endpoints, single-method updates are the way to go, in order to reduce the chances of two concurrent updates overwriting one another's changes.
在多线程环境中,例如具有多个REST端点的Java后端,可以使用单方法更新,以减少两个并发更新覆盖彼此更改的机会。
Example: given a document like this: { _id: "ID1", field1: "a string", field2: 10.0 }
and two different threads concurrently updating it...
例如:给定这样的文档:{_id:“ID1”、field1:“一个字符串”、field2: 10.0}和两个不同的线程同时更新它……
With MongoTemplate
it would look somewhat like this:
对于MongoTemplate,它看起来是这样的:
THREAD_001 THREAD_002
| |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
| |
| |
and the final state for the document is always { _id: "ID1", field1: "another string", field2: 15.0 }
since each thread is accesing the DB only once and only the specified field is changed.
而文档的最终状态总是{_id:“ID1”、field1:“另一个字符串”、field2: 15.0},因为每个线程只修改DB一次,并且只修改指定的字段。
Whereas the same case scenario with MongoRepository
would look like this:
而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)
| |
| |
and the final document being either { _id: "ID1", field1: "another string", field2: 10.0 }
or { _id: "ID1", field1: "a string", field2: 15.0 }
depending on which save
operation hits the DB first.
最后一个文档是{_id:“ID1”,field1:“另一个字符串”,field2: 10.0}或{_id:“ID1”,field1:“一个字符串”,field2: 15.0},这取决于保存操作对DB的优先级。
So I'd say that MongoTemplate
is a better option, unless you have a very elaborated POJO model or need the custom queries capabilities of MongoRepository
for some reason.
因此,我认为MongoTemplate是一个更好的选择,除非您有一个非常详细的POJO模型,或者出于某种原因需要定制查询功能。
#1
101
"Convenient" and "powerful to use" are contradicting goals to some degree. Repositories are by far more convenient that the templates but the latter of course give you more fine-grained control over what to execute.
“方便”和“强大的使用”在某种程度上与目标相矛盾。到目前为止,存储库比模板要方便得多,但后者当然会为您提供对执行内容的更细粒度控制。
As the repository programming model is available for multiple Spring Data modules you find more in-depth documentation of it in the general section of the Spring Data MongoDB reference docs.
由于存储库编程模型可用于多个Spring数据模块,因此您可以在Spring Data MongoDB参考文档的一般部分找到更深入的文档。
TL;DR
博士TL;
We generally recommend the following approach:
我们一般建议采用以下方法:
- Start with the repository abstract and just declare simple queries using the query derivation mechanism or manually defined queries.
- 从存储库抽象开始,使用查询派生机制或手动定义查询声明简单查询。
- For more complex queries, add manually implemented methods to the repository (as documented here). For the implementation use
MongoTemplate
. - 对于更复杂的查询,请向存储库中添加手动实现的方法(如本文所述)。对于实现使用MongoTemplate。
Details
细节
For your example this would look something like this:
在你的例子中,这个看起来是这样的:
-
Define an interface for your custom code:
为您的自定义代码定义一个接口:
interface CustomUserRepository { List<User> yourCustomMethod(); }
-
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 } }
-
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.
通过这种方式,您基本上可以做出选择:所有容易声明的内容都进入UserRepository,所有更好地手动实现的内容都进入CustomUserRepository。这里记录了定制选项。
#2
11
This answer may be a bit delayed, but I would recommend avoiding the whole repository route. You get very little implemented methods of any great practical value. In order to make it work you run into the Java configuration nonsense which you can spend days and weeks on without much help in the documentation.
这个答案可能有点延迟,但是我建议避免使用整个存储库路径。你得到的实现方法很少有任何实际价值。为了使它正常工作,您会遇到Java配置无意义的问题,您可以在文档中不需要太多帮助的情况下花费数天或数周的时间。
Instead, go with the MongoTemplate
route and create your own Data access layer which frees you from the configuration nightmares faced by Spring programmers. MongoTemplate
is really the savior for engineers who are comfortable architecting their own classes and interactions since there is lot of flexibility. The structure can be something like this:
相反,使用MongoTemplate路由创建您自己的数据访问层,从而将您从Spring程序员面临的配置噩梦中解放出来。MongoTemplate是那些轻松构建自己的类和交互的工程师的救星,因为它具有很大的灵活性。结构可以是这样的:
- Create a
MongoClientFactory
class that will run at the application level and give you aMongoClient
object. You can implement this as a Singleton or using an Enum Singleton (this is thread safe) - 创建将在应用程序级别上运行的MongoClientFactory类,并为您提供MongoClient对象。您可以将其实现为单例或使用Enum单例(这是线程安全的)
- 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
- 创建一个数据访问基类,从中可以继承每个域对象的数据访问对象)。基类可以实现创建MongoTemplate对象的方法,您可以将特定的方法用于所有的DB访问
- Each data access class for each domain object can implement the basic methods or you can implement them in the base class
- 每个域对象的每个数据访问类都可以实现基本方法,或者您可以在基类中实现它们。
- The Controller methods can then call methods in the Data access classes as needed.
- 然后,控制器方法可以根据需要调用数据访问类中的方法。
#3
0
FWIW, regarding updates in a multi-threaded environment:
FWIW,关于多线程环境中的更新:
-
MongoTemplate
provides out-of-the-boxupdateFirst
,updateMulti
,findAndModify
,upsert
... methods which allow you to modify a document in a single operation. TheUpdate
object used by these methods also allows you to target only the relevant fields. - MongoTemplate提供开箱即用的updateFirst、updateMulti、findAndModify、upsert…方法,允许您在单个操作中修改文档。这些方法使用的Update对象还允许您只针对相关字段。
-
MongoRepository
only gives you the basicfind
,insert
,save
,delete
operations, which work with POJOs containing all the fields. This forces you to either update the documents in several steps (find
the document to update, modify the relevant fields from the returned POJO, and thensave
it), or define your own update queries by hand using@Query
. - MongoRepository只提供基本的查找、插入、保存、删除操作,这些操作与包含所有字段的pojo一起使用。这将迫使您在几个步骤中更新文档(找到要更新的文档,从返回的POJO中修改相关字段,然后保存它),或者使用@Query手工定义您自己的更新查询。
In a multi-threaded environment, like e.g. a Java back-end with several REST endpoints, single-method updates are the way to go, in order to reduce the chances of two concurrent updates overwriting one another's changes.
在多线程环境中,例如具有多个REST端点的Java后端,可以使用单方法更新,以减少两个并发更新覆盖彼此更改的机会。
Example: given a document like this: { _id: "ID1", field1: "a string", field2: 10.0 }
and two different threads concurrently updating it...
例如:给定这样的文档:{_id:“ID1”、field1:“一个字符串”、field2: 10.0}和两个不同的线程同时更新它……
With MongoTemplate
it would look somewhat like this:
对于MongoTemplate,它看起来是这样的:
THREAD_001 THREAD_002
| |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
| |
| |
and the final state for the document is always { _id: "ID1", field1: "another string", field2: 15.0 }
since each thread is accesing the DB only once and only the specified field is changed.
而文档的最终状态总是{_id:“ID1”、field1:“另一个字符串”、field2: 15.0},因为每个线程只修改DB一次,并且只修改指定的字段。
Whereas the same case scenario with MongoRepository
would look like this:
而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)
| |
| |
and the final document being either { _id: "ID1", field1: "another string", field2: 10.0 }
or { _id: "ID1", field1: "a string", field2: 15.0 }
depending on which save
operation hits the DB first.
最后一个文档是{_id:“ID1”,field1:“另一个字符串”,field2: 10.0}或{_id:“ID1”,field1:“一个字符串”,field2: 15.0},这取决于保存操作对DB的优先级。
So I'd say that MongoTemplate
is a better option, unless you have a very elaborated POJO model or need the custom queries capabilities of MongoRepository
for some reason.
因此,我认为MongoTemplate是一个更好的选择,除非您有一个非常详细的POJO模型,或者出于某种原因需要定制查询功能。