1. Overzicht
In deze tutorial zullen we enkele van de kernfuncties van Spring Data MongoDB verkennen:indexering, algemene annotaties en converters.
2. Indexen
2.1. @Indexed
Deze annotatie markeert het veld als geïndexeerd in MongoDB:
@QueryEntity
@Document
public class User {
@Indexed
private String name;
...
}
Nu de naam veld is geïndexeerd – laten we eens kijken naar de indexen in de MongoDB-shell:
db.user.getIndexes();
Dit is wat we krijgen:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.user"
}
]
Het zal ons misschien verbazen dat er geen teken is van de naam veld overal!
Dit komt omdat, vanaf Spring Data MongoDB 3.0, het automatisch aanmaken van indexen standaard is uitgeschakeld .
We kunnen dat gedrag echter veranderen door autoIndexCreation() expliciet te negeren methode in onze MongoConfig :
public class MongoConfig extends AbstractMongoClientConfiguration {
// rest of the config goes here
@Override
protected boolean autoIndexCreation() {
return true;
}
}
Laten we nogmaals de indexen in de MongoDB-shell bekijken:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.user"
},
{
"v" : 1,
"key" : {
"name" : 1
},
"name" : "name",
"ns" : "test.user"
}
]
Zoals we kunnen zien, hebben we deze keer twee indexen - een daarvan is _id – die standaard is gemaakt vanwege de @Id annotatie en de tweede is onze naam veld.
Als alternatief als we Spring Boot gebruiken, kunnen we de spring.data.mongodb.auto-index-creation instellen eigenschap naar true .
2.2. Programmatisch een index maken
We kunnen ook programmatisch een index maken:
mongoOps.indexOps(User.class).
ensureIndex(new Index().on("name", Direction.ASC));
We hebben nu een index gemaakt voor het veld naam en het resultaat zal hetzelfde zijn als in de vorige sectie.
2.3. Samengestelde indexen
MongoDB ondersteunt samengestelde indexen, waarbij een enkele indexstructuur verwijzingen naar meerdere velden bevat.
Laten we een snel voorbeeld bekijken met samengestelde indexen:
@QueryEntity
@Document
@CompoundIndexes({
@CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
//
}
We hebben een samengestelde index gemaakt met de e-mail en leeftijd velden. Laten we nu eens kijken naar de daadwerkelijke indexen:
{
"v" : 1,
"key" : {
"email.id" : 1,
"age" : 1
},
"name" : "email_age",
"ns" : "test.user"
}
Merk op dat een DBref veld kan niet worden gemarkeerd met @Index – dat veld kan alleen deel uitmaken van een samengestelde index.
3. Algemene annotaties
3.1. @Transient
Zoals we zouden verwachten, sluit deze eenvoudige annotatie uit dat het veld in de database wordt bewaard:
public class User {
@Transient
private Integer yearOfBirth;
// standard getter and setter
}
Laten we de gebruiker invoegen met het instellingsveld yearOfBirth :
User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);
Als we nu naar de staat van de database kijken, zien we dat de gearchiveerde yearOfBirth is niet opgeslagen:
{
"_id" : ObjectId("55d8b30f758fd3c9f374499b"),
"name" : "Alex",
"age" : null
}
Dus als we vragen en controleren:
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getYearOfBirth()
Het resultaat is null .
3.2. @Field
@Field geeft de sleutel aan die moet worden gebruikt voor het veld in het JSON-document:
@Field("email")
private EmailAddress emailAddress;
Nu e-mailadres wordt opgeslagen in de database met behulp van de sleutel e-mail:
User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);
En de staat van de database:
{
"_id" : ObjectId("55d076d80bad441ed114419d"),
"name" : "Brendan",
"age" : null,
"email" : {
"value" : "[email protected]"
}
}
3.3. @PersistenceConstructor en @Value
@PersistenceConstructor markeert een constructor, zelfs een die door een pakket is beveiligd, als de primaire constructor die wordt gebruikt door de persistentielogica. De constructorargumenten worden op naam toegewezen aan de sleutelwaarden in het opgehaalde DBObject .
Laten we eens kijken naar deze constructor voor onze Gebruiker klas:
@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
this.name = name;
this.age = age;
this.emailAddress = emailAddress;
}
Let op het gebruik van de standaard Spring @Value annotatie hier. Met behulp van deze annotatie kunnen we de Spring Expressions gebruiken om de waarde van een sleutel die uit de database is opgehaald, te transformeren voordat deze wordt gebruikt om een domeinobject te construeren. Dat is hier een zeer krachtige en zeer nuttige functie.
In ons voorbeeld als leeftijd niet is ingesteld, wordt deze ingesteld op 0 standaard.
Laten we nu kijken hoe het werkt:
User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);
Onze database ziet er als volgt uit:
{
"_id" : ObjectId("55d074ca0bad45f744a71318"),
"name" : "Alex",
"age" : null
}
Dus de leeftijd veld is null , maar wanneer we het document opvragen en leeftijd ophalen :
mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();
Het resultaat is 0.
4. Converters
Laten we nu eens kijken naar een andere zeer nuttige functie in Spring Data MongoDB - converters, en specifiek naar de MongoConverter .
Dit wordt gebruikt om de toewijzing van alle Java-typen aan DBObjects . af te handelen bij het opslaan en opvragen van deze objecten.
We hebben twee opties – we kunnen werken met MappingMongoConverter – of SimpleMongoConverter in eerdere versies (dit was verouderd in Spring Data MongoDB M3 en de functionaliteit is verplaatst naar MappingMongoConverter ).
Of we kunnen onze eigen aangepaste converter schrijven. Om dat te doen, zouden we de Converter . moeten implementeren interface en registreer de implementatie in MongoConfig.
Laten we eens kijken naar een snel voorbeeld . Zoals we hebben gezien in een deel van de JSON-uitvoer hier, hebben alle objecten die in een database zijn opgeslagen het veld _class die automatisch wordt opgeslagen. Als we dat specifieke veld echter willen overslaan tijdens persistentie, kunnen we dat doen met een MappingMongoConverter .
Ten eerste - hier is de implementatie van de aangepaste converter:
@Component
public class UserWriterConverter implements Converter<User, DBObject> {
@Override
public DBObject convert(User user) {
DBObject dbObject = new BasicDBObject();
dbObject.put("name", user.getName());
dbObject.put("age", user.getAge());
if (user.getEmailAddress() != null) {
DBObject emailDbObject = new BasicDBObject();
emailDbObject.put("value", user.getEmailAddress().getValue());
dbObject.put("email", emailDbObject);
}
dbObject.removeField("_class");
return dbObject;
}
}
Merk op hoe we gemakkelijk het doel kunnen bereiken om niet vol te houden _class door het veld hier specifiek te verwijderen.
Nu moeten we de aangepaste converter registreren:
private List<Converter<?,?>> converters = new ArrayList<Converter<?,?>>();
@Override
public MongoCustomConversions customConversions() {
converters.add(new UserWriterConverter());
return new MongoCustomConversions(converters);
}
We kunnen natuurlijk hetzelfde resultaat bereiken met XML-configuratie als dat nodig is:
<bean id="mongoTemplate"
class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongo" ref="mongo"/>
<constructor-arg ref="mongoConverter" />
<constructor-arg name="databaseName" value="test"/>
</bean>
<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
<mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>
Als we nu een nieuwe gebruiker opslaan:
User user = new User();
user.setName("Chris");
mongoOps.insert(user);
Het resulterende document in de database bevat niet langer de klasse-informatie:
{
"_id" : ObjectId("55cf09790bad4394db84b853"),
"name" : "Chris",
"age" : null
}