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. Good points/examples.
MongoTemplate provides a simple way for you to save, update, and delete your domain objects and map those objects to documents stored in MongoDB. You can save, update and delete the object as shown below. MongoOperations is the interface that MongoTemplate implements.
MongoRepository is an interface provided by Spring Data in the package org. springframework. data. mongodb.
The MongoTemplate class is the primary implementation of MongoOperations interface which specifies the basic set of MongoDB operations. We can also use MongoRepository interface to perform MongoDB operations. The implementation class of MongoRepository uses MongoTemplate bean at run time.
"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.
As the repository programming model is available for multiple Spring Data modules, you'll find more in-depth documentation for it in the general section of the Spring Data MongoDB reference docs.
TL;DR
We generally recommend the following approach:
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.
FWIW, regarding updates in a multi-threaded environment:
MongoTemplate
provides "atomic" out-of-the-box operations updateFirst
, updateMulti
, findAndModify
, upsert
... which allow you to modify a document in a single operation. The Update
object used by these methods also allows you to target only the relevant fields.MongoRepository
only gives you the basic CRUD operations find
, insert
, save
, delete
, which work with POJOs containing all the fields. This forces you to either update the documents in several steps (1. find
the document to update, 2. modify the relevant fields from the returned POJO, and then 3. save
it), or define your own update queries by hand using @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.
Example: given a document like this: { _id: "ID1", field1: "a string", field2: 10.0 }
and two different threads concurrently updating it...
With MongoTemplate
it would look somewhat like this:
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.
Whereas the same case scenario with MongoRepository
would look like this:
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 last.
(NOTE: Even if we used Spring Data's @Version
annotation as suggested in the comments, not much would change: one of the save
operations would throw an OptimisticLockingFailureException
, and the final document would still be one of the above, with only one field updated instead of both.)
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.
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.
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:
MongoClientFactory
class that will run at the application level and give you a MongoClient
object. You can implement this as a Singleton or using an Enum Singleton (this is thread safe)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With