Allez, du courage nous y sommes presque, avant d’exécuter l’application et de constater notre premier résultat ! D’ailleurs, je vous conseille d’adapter dès maintenant la classe principale, nommée dans mon cas BlogDataLayerApplication, pour constater plus tard le résultat des opérations que nous allons effectuer.
Ajoutez l’interface CommandLineRunner puis implémentez la méthode public void run(String… args) throws Exception. Au sein de cette méthode, nous pourrons écrire du code qui sera exécuté au démarrage de l’application.
Enfin, ajoutez 2 attributs de classe, le logger et la référence au repository. Voilà à quoi devrait ressembler votre classe :
package com.openclassrooms.blogdatalayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.blogdatalayer.model.Post;
import com.openclassrooms.blogdatalayer.repository.PostRepository;
@SpringBootApplication
public class BlogDataLayerApplication implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(BlogDataLayerApplication.class);
@Autowired
private PostRepository postRepository;
public static void main(String[] args) {
SpringApplication.run(BlogDataLayerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info(“Start !”);
}
}
C’est bon pour vous ? Top ! Voyons comment réaliser une première requête à notre base de données.
Récupérez une donnée de façon unitaire
Rappelons notre besoin fonctionnel. Notre plateforme de blogging doit afficher nos articles et nos tutoriels. Techniquement, cela signifie récupérer l’information qui est stockée dans la base de données, puis l’afficher.
Étant donné que nous ne nous occupons pas de l’affichage dans ce cours, notre tâche est donc la suivante : récupérer un article et logger son contenu dans la console.
Vous êtes prêt ?! Retrouvez-moi dans la démonstration qui suit :
Revenons ensemble sur le code que nous avons créé :
package com.openclassrooms.blogdatalayer;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.blogdatalayer.model.Post;
import com.openclassrooms.blogdatalayer.repository.PostRepository;
@SpringBootApplication
public class BlogDataLayerApplication implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(BlogDataLayerApplication.class);
@Autowired
private PostRepository postRepository;
public static void main(String[] args) {
SpringApplication.run(BlogDataLayerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Optional<Post> p = postRepository.findById("6177a31824f1d205e0b0692d");
if (p.isPresent()) {
logger.info(p.get().getContent());
} else {
logger.info("Post not found");
}
}
}
En résumé, qu’avons-nous fait ? Très peu de choses finalement !
Premièrement, nous avons interrogé la base MongoDB avec la méthode findById() ligne 30.
Deuxièmement, lignes 31 à 35, nous avons vérifié si l’objet retourné contient une valeur. Si oui, nous loggons le contenu du post. Sinon, nous loggons un message standard “Post not found”.
Mais comment se fait-il que findById() fonctionne alors que nous n’avons pas défini la requête ?
C’est tout l’intérêt d’utiliser l’interface MongoRepository ! Certaines opérations sont déjà implémentées et prêtes à l’emploi. Ainsi, sans effort, nous avons pu récupérer un article par son id.
À vous de jouer
Récupérez un tutoriel via la méthode findById de l’interface TutorialRepository. Cela devrait être rapide !
Retrouvez le corrigé de cet exercice dans le repository GitHub indiqué à la fin du chapitre.
Récupérez un ensemble de données
Tout comme nous pouvons récupérer un article unitaire, nous pouvons également récupérer plusieurs articles. Vous l’avez peut-être deviné, une méthode est déjà présente : findAll().
package com.openclassrooms.blogdatalayer;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.blogdatalayer.model.Post;
import com.openclassrooms.blogdatalayer.repository.PostRepository;
@SpringBootApplication
public class BlogDataLayerApplication implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(BlogDataLayerApplication.class);
@Autowired
private PostRepository postRepository;
public static void main(String[] args) {
SpringApplication.run(BlogDataLayerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
List<Post> allPosts = postRepository.findAll();
allPosts.stream().forEach((post) -> logger.info(post.getName()));
}
}
À la ligne 28, j’appelle la méthode findAll().
À la ligne 29, je parcours ma liste et je log le nom de chaque article (je ne vous fais pas l’offense de vous expliquer ce code ).
Rien de plus.
Bon, récupérer un élément par son id, ou récupérer tous les éléments, ne suffisent peut-être pas pour développer toutes les fonctionnalités. Voyons comment aller plus loin dans la section suivante.
À vous de jouer
Récupérez tous les tutoriels via la méthode findAll() de l’interface TutorialRepository. Cela devrait être (très) rapide !
Retrouvez le corrigé de cet exercice dans le repository GitHub indiqué à la fin du chapitre.
Personnalisez vos requêtes
Maintenant, imaginons que nous voulions récupérer un article à partir de son nom. Avons-nous déjà une solution prête à l’emploi ? Tout à fait !
Observez le code ci-dessous. Par simplicité, je vous ai remis uniquement la méthode run :
@Override
public void run(String... args) throws Exception {
Post p1 = new Post();
p1.setName("Welcome!");
Example<Post> example = Example.of(p1);
Optional<Post> p = postRepository.findOne(example);
if (p.isPresent()) {
logger.info(p.get().getContent());
} else {
logger.info("Post not found");
}
}
J’ai créé un nouvel objet de type Post et j’ai défini le nom de ce dernier. Puis, grâce à la ligne Example<Post> example = Example.of(p1);, j’ai créé un objet de type Example qui embarque l’objet précédemment créé.
Ensuite, je peux me servir de cet objet avec la méthode findOne(). Cette méthode me retournera le document qui a ce nom !
Mais, on n’a pas vu la méthode findOne dans la documentation de l’interface MongoRepository ?
Tout à fait, car MongoRepository étend une autre interface : QueryByExampleExecutor. Cette dernière fournit la méthode findOne(). Vous pouvez consulter sa documentation via cette page.
Mais ce n’est pas tout, Spring Data MongoDB en a encore dans le ventre ! Laissez-moi vous parler des requêtes dérivées, communément appelées Derived Queries, en anglais.
Créer un objet Example est fastidieux quand votre unique objectif est de récupérer un document par son nom. Vous gagnerez en efficacité en utilisant une méthode déjà implémentée, comme l’est findById. Cette méthode s'appellerait alors findByName !
Vous l’avez demandé, vous l’obtenez ! Observez la modification dans l’interface PostRepository :
package com.openclassrooms.blogdatalayer.repository;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.openclassrooms.blogdatalayer.model.Post;
@Repository
public interface PostRepository extends MongoRepository<Post, String> {
public List<Post> findByName(String name);
}
Au sein de l’interface, j’ai rajouté un nouveau prototype de méthode dont le nom est composé par des mots clés : findBy et Name :
findBy fait référence à l’action de récupérer un document par un critère donné ;
Name correspond au nom de l’attribut dans le document qui me sert de critère.
Et comment implémenter cette méthode ?
Nul besoin d’écrire le code de cette méthode ! Spring Data va analyser le prototype et générer le code nécessaire à l’exécution du traitement. Côté exécution, voici mon code :
@Override
public void run(String... args) throws Exception {
List<Post> result = postRepository.findByName("Welcome!");
result.stream().forEach((post) -> logger.info(post.getName()));
}
Je récupère le résultat de l’appel à findByName dans un objet typé List<Post>, car il peut y avoir plusieurs documents qui ont le même nom. Ensuite, je parcours la liste et je log le nom de chaque post de la liste.
Cette façon de faire est généralisable à tous les attributs de votre classe grâce à la syntaxe : findBy + nom de l’attribut. Par exemple : findByDate.
Cette situation est une transition parfaite pour vous parler des index. Dans le cas précédent, j’ai défini la méthode findByName. Toutefois, je me rends compte que plusieurs articles peuvent avoir le même nom, ce qui ne me convient pas car en réalité les noms des articles doivent être uniques !
Comment faire ?
En ajoutant un index !
Au-delà de sa plus-value en termes de performances, l’index nous permet de définir des contraintes d’unicité. Nous pouvons ainsi dire à MongoDB qu’un champ du document doit être unique pour chaque document.
Retournons dans notre classe Post et définissons l’index et la contrainte d’unicité :
@Indexed(unique = true)
private String name;
L’annotation @Indexed permet de définir un index sur le champ name, et l’attribut d’annotation unique permet de rendre unique ce champ.
Nous devons également activer la création automatique des index au niveau de la configuration, en ajoutant la ligne suivante dans le fichier application.properties :
spring.data.mongodb.auto-index-creation=true
Sans cela, nous serions obligés de créer les index dans la base MongoDB directement.
Nous arrivons à la conclusion de ce chapitre, mais je ne vous ai pas encore tout dit sur les requêtes dérivées. Spring Data MongoDB vous permet d’écrire des prototypes de méthodes avec de nombreux critères de requêtes.
Tous ces critères sont décrits dans le “Table 15. Supported keywords for query methods” de la documentation officielle.
Vous pouvez par exemple récupérer tous les articles écrits après une date donnée :
List<Post> findByDateAfter(Date date);
Ou récupérer tous les articles dont le contenu contient une certaine chaîne de caractères :
List<Post> findByContentContaining(String text);
À vous de jouer
Exécutez les opérations ci-dessous pour Tutorial :
Définissez la contrainte d’unicité pour le name du Tutorial.
Récupérez un Tutorial par son name. Pour cela, rajoutez une nouvelle méthode dans l’interface TutorialRepository.
Récupérez tous les tutoriels dont la courte description contient un mot donné.
N’hésitez pas à vérifier votre travail en consultant le repository GitHub du cours à la branche p2c2. Il contient l’intégralité du code de ce chapitre.
En résumé
MongoRepository fournit diverses méthodes utiles comme findById ou findAll() pour récupérer vos documents unitairement ou par lot.
Vous pouvez ajouter de nouvelles méthodes à votre interface grâce aux Derived Queries.
MongoDB est capable de gérer des index. Ces derniers optimisent les performances des requêtes, et rendent unique un champ du document.
Vous savez désormais comment implémenter une lecture de données simple. La prochaine étape ? Implémenter une lecture de données complexe dans le prochain chapitre !