Mongo Spring #Document annotation - spring

I'm trying to work with MongoTemplate and Spring and while looking on some other peoples code I was that everyone uses #Document annotation.
I did not used it at all up until not and everything worked fine for me.
I'm afraid I'm missing something and could not find any specific detailed information about the benefits of #Document annotation.

#Document is an annotation provided by Spring data project.
It is used to identify a domain object, which is persisted to MongoDB.
So you can use it to map a Java class into a collection inside MongoDB.
If you don't use Spring Data, you don't need this annotation.
I wrote a German blog post about how to use Spring Data for MongoDB - #Document is used there also:
https://agile-coding.blogspot.com/2020/10/keine-ahnung-von-mongodb-dann-nimm.html

This annotation serves only for specifying collection properties.
You can basically create simple class without any annotation needed.
Document annotation serves you for example when you are not happy with autogenerated collection name. If you do not specify #Document annotation, you still have the same result, for example
[
{
"_id": {"$oid": "62b43525de57ec7dec41a286"},
"_class": "com.example.demo.Person",
"name": "Adam"
}
]
This annotation may however be important for some kind of annotation based process to target which document are in use (maybe for indexing and so on)
Same goes for Id annotation:
if you do not provide any id specification, Id field is automatically added by mongo
if you have field named as "id" of type BigInteger, ObjectId or String, then this field is automatically populated after insert
From the docs https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo-template.id-handling
11.5.1. How the _id Field is Handled in the Mapping Layer
MongoDB requires that you have an _id field for all documents. If you
do not provide one, the driver assigns an ObjectId with a generated
value. When you use the MappingMongoConverter, certain rules govern
how properties from the Java class are mapped to this _id field:
A property or field annotated with #Id (org.springframework.data.annotation.Id) maps to the _id field.
A property or field without an annotation but named id maps to the _id field.
The following outlines what type conversion, if any, is done on the
property mapped to the _id document field when using the
MappingMongoConverter (the default for MongoTemplate).
If possible, an id property or field declared as a String in the Java class is converted to and stored as an ObjectId by using a Spring
Converter<String, ObjectId>. Valid conversion rules are delegated to
the MongoDB Java driver. If it cannot be converted to an ObjectId,
then the value is stored as a string in the database.
An id property or field declared as BigInteger in the Java class is converted to and stored as an ObjectId by using a Spring
Converter<BigInteger, ObjectId>.
If no field or property specified in the previous sets of rules is
present in the Java class, an implicit _id file is generated by the
driver but not mapped to a property or field of the Java class.
When querying and updating, MongoTemplate uses the converter that
corresponds to the preceding rules for saving documents so that field
names and types used in your queries can match what is in your domain
classes.
Some environments require a customized approach to map Id values such
as data stored in MongoDB that did not run through the Spring Data
mapping layer. Documents can contain _id values that can be
represented either as ObjectId or as String. Reading documents from
the store back to the domain type works just fine. Querying for
documents via their id can be cumbersome due to the implicit ObjectId
conversion. Therefore documents cannot be retrieved that way. For
those cases #MongoId provides more control over the actual id mapping
attempts. Example 62. #MongoId mapping
public class PlainStringId { #MongoId String id; }
public class PlainObjectId { #MongoId ObjectId id; }
public class StringToObjectId { #MongoId(FieldType.OBJECT_ID) String id; }
The id is treated as String without further conversion. The id is
treated as ObjectId. The id is treated as ObjectId if the given
String is a valid ObjectId hex, otherwise as String. Corresponds to
#Id usage.

Related

Spring Data Mongo Linked Document Storage

Is there a pattern for Spring Data Mongo to support persisting a link to a separate document in separate collection and having it re-hydrate automagically when its pulled back out of the database?
#Document
class Person <-- Saves to the Person Collection
#id
UUID id
String name
Address address
#Document
class Address --
#id
UUID id
String address1
...
Calling save(person) I'd want the address property in the database to reflect the Address Id, and have the address object persisted to the address collection. When I pull the Person back out, Address would be a fully hydrated (or maybe lazily?) and accessible.
Spring Data Mongo 3.1
Spring Boot 2.4
Groovy 2.5
JDK11
At the time of writing Spring Data MongoDB 3.2 only supports linking documents via DBRefs. Those however follow a fixed structure so that the value representing the link within the target document looks like this
{ "$ref" : <value>, "$id" : <value>, "$db" : <value> }
#DBRef allows lazy loading via its lazy attribute. Please have a look at the reference documentation
The upcoming Spring Data MongoDB 3.3 release will extend the support for linking documents to cover the above mentioned use case. Still the linked document needs to be persisted on its own. #DocumentReference allows to link Address via the id property as outlined below.
#Document
class Person {
#Id
String id;
#DocumentReference
Address address;
}
{
"_id" : "p1457",
"name" : "...",
"address" : "a4711"
}
#DocumentReference will also support lazy loading and can be used to link a collection of documents.
Please find the full documentation here.

Handling filtering by a particular field in JPA Spring Boot

I want to implement a filtering feature based on the properties of an entity that I have stored in my db.
I'm using a JPA query to do so. The problem that I'm facing is concerned with entity manager which requires the class of the object that is required to return.
public List<CountryEntity> getSortedCountries(String field, String type) {
return entityManager.createQuery(GET_ALL_SORTED.concat(field).concat(" " + type), CountryEntity.class).getResultList();
}
When I select only one field, let's say the name of the country, the query returns a String and not an object of type CountryEntity.
What is the best approach to handle this problem? Should I create classes for every single case or is there another way that I'm missing?

How do I map "id" property into "id" field in Mongo without #Field annotation?

I have multiple classes with the id field. I would like to store their instances in MongoDB using spring-data-mongodb. I would like to map id property in these classes to id field in Mongo.
So here is what my classes look like:
public class Entity {
private final String id; // = 42
...
}
And here is what I am expecting to be in Mongo collection:
{
"_id": ObjectId("5fba805dfdaaa760974d45de"),
"id": "42"
}
By default, spring-mongodb maps id property to _id field in Mongo. I know that the simplest way to avoid this is to put #org.springframework.data.mongodb.core.mapping.Field("id") annotation on id property in a Java class. But I prefer not to use this annotation since I would like to keep my model independent of Mongo, or Spring, or whatever. Which options are possible here?
Here is what I have tried or checked:
Registering custom AbstractMongoEventListener in order to modify Mongo documents just before they are written to Mongo, or just after they have been read from Mongo. It does not work for me since custom listener is called only during get and insert operations, but not during the update or upsert operations (see discussion here for details).
Providing custom FieldNamingStrategy — it does not work since in the spring-data-mongodb code they use strategy only if field name is not id or _id.
Providing custom converter for each of my classes. I believe it should work. But this approach seems to be too complicated since I have many classes with many properties in each of them and I'm not sure I would like to have many converters with boilerplate code inside.
Any help would be appreciated.
Don't you think that id with _id is already confusing?
If you have a strong valid reason to have an id (different than DB one); give it a name .e.g: version_id, old_id, other_services_id ... some meaningful name.
And yes using #Field in your case is the simplest way

is it possible to have conditional #Transient field?

Let's say if I have an Entity named person with lots of information including SSN. When other user query this person, I want to show a 'lite' version of person Entity. I could've done so by annotating SSN with #Transient, but that means the person himself would not get this field too. Is it possible to reuse the same Entity but return two different json to client? I'm using spring boot.
First of all #Transient just means that the value, the SSN in your case, won't be persisted to the database.
As for your problem annotations are static and cannot be applied dynamically.
You have 2 Options:
Define a new View class for your user.
Look at JacksonJsonViews

How to change which field is used as _id without annotations in Spring data MongoDB

I have some classes which are being generated from a WSDL using the CXF wsdl2java tool.
I would like to store instances of these classes in a MongoDB database, using Spring data MongoDB.
The default mapping is acceptable for this, except for one thing:
I would like to change which field is used as _id.
Normally this is done with a annotation like #Id.
But because these classes are generated, I would like to do this without an annotation.
Is there a (correct?) way to do this?
So my generated class is:
class Simple {
String businessId;
String otherfield1;
.
String otherfield999;
}
And I would like Spring data MongoDB to use 'businessId' as the '_id' field in MongoDB, without changing the 'Simple' class by adding an annotation.
Thanks!
That's currently not supported. The property either needs to be id, _id or annotated with #Id.

Resources