Dans cette application, il n’y a pas d’utilisation du framework Hibernate.
Ce projet est une application SpringBoot basique permettant de gérer une base de données d’acteurs avec les opérations CRUD standards. Il utilise la base de données de test Sakila installée avec Mysql. Personnellement, je ne l’installe pas. Peu importe car pour les tests avec JUnit vous n’en aurez pas besoin !
Côté front : client html javascript natif utilisant les bibliothèques jquery & bootstrap. Côté back : application Java SpringBoot qui implémente les principes du MVC avec une couche DAO qui communique avec Mysql.
L’objectif de ce tutoriel est de fournir un projet permettant de travailler les aspects suivants :
Pour faire fonctionner ce projet, l’installation des composants suivants est conseillée :
Il est aussi recommandé d’avoir fait l’étude du projet java-springboot-decouverte (accessible ici)
Pour créer ce projet dans Eclipse, faire un import du projet Maven.
En plus des éléments classiques vus dans le projet java-springboot-decouverte, nous avons ajouté des dépendances vers :
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> </dependencies>
Remarque : Il est possible de forcer l’utilisation d’une version d’une des dépendances en ajoutant une balise **** contenant cette l'identifiant de la version à utiliser. Dans le cas où ce n'est pas spécifié, c'est SpringBoot qui détermine quel est la meilleure version à utiliser.
Vous trouverez dans ce fichier les informations de configuration pour la base de données et pour le sytème de log.
# connection base spring.datasource.url=jdbc:mysql://localhost/sakila?useSSL=false spring.datasource.username=admin spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.jdbc.Driver # log logging.level.root=INFO logging.file="c:\logSpring.log"
Remarque : Si l’application ne fonctionne pas, c’est peut-être dû à vos paramètres mysql ?! C’est ici qu’il vous faudra travailler pour corriger le problème.
Notre code source est organisé selon les principes du MVC (Model View Controller) :
Des éléments relatifs à la Vue sont présents dans le répertoire src/main/resources/static (partie front). Pour nous pas besoin de les utiliser.
Il s’agit de la classe de notre modèle de données. Sa structure correspond à la structure de la table associée dans la base de données. On peut la considérer comme un Bean java standard. Il contient :
La couche DAO est la couche qui gère la persistance des données. Cette couche apporte les méthodes CRUD classiques pour les classes du modèle associées.
Il s’agit d’une interface java classique qui contient les méthodes CRUD pour créer, modifier, supprimer et retrouver des données de type Actor dans la base de données.
JdbcActorDAO est la classe d’implémentation associée à l’interface ActorDAO.
Elle porte le code capable de produire et exécuter les requêtes SQL nécessaires à la persistance des données de type Actor.
Elle annotée de @Repository permettant au système de résolution des dépendances d’identifier les classes “DAO”.
@Repository public class JdbcActorDAO implements ActorDAO { private final Logger log = LoggerFactory.getLogger(this.getClass()); private DataSource datasource;
Pour construire l’attribut datasource, qui gère la connexion avec la base de données, on utilisera la classe JdbcTemplate (fournie par Spring). Ainsi on obtient une connexion automatiquement configurée avec les informations du fichier application.properties.
@Autowired public JdbcActorDAO(JdbcTemplate jdbcTemplate) { this.datasource = jdbcTemplate.getDataSource(); }
L’annotation @Autowired délègue à Spring la gestion du cycle de vie de cet attribut passé au constructeur qu’elle précède. Avec cette annotation, on demande à Spring de trouver et instancier la classe fournie en argument de notre constructeur.
C’est la couche métier de l’application.
C’est ici qu’est décrit le comportement des classes (diagramme des séquences), les transactions, les relations entre les classes. Le service sert de transition, est appelé par le contrôleur et agit sur le modèle.
Bien qu’il fasse partie du modèle, il est placé dans le package service.
Dans un exemple aussi simple que celui-ci, la classe ActorService n’a que peu d’intérêt.
On y voit une classe “Passe Plat” qui ne fait que relayer les méthodes de la classe DAO. Pour déclarer son existence à Spring, on la précède de l’annotation @Service
@Service public class ActorService { @Autowired private ActorDAO dao;
Remarque : on voit ici une autre utilisation de l’annotation @Autowired faite sur un attribut de la classe. L’idée est la même : déléguer la gestion du cycle de vie de dao à Spring. En scannant notre code, Spring cherchera la classe la plus adaptée à être associée à cet attribut et appellera son constructeur au bon moment (durant l’exécution de notre programme).
Remarque additionnel : L’attribut dao est une interface. Pour résoudre “l’injection de dépendance” décrite ci-dessus, Spring créera une instance de la JdbcActorDAO qui implémente cette interface qu’il considèrera comme la meilleure classe pour faire le travail (C’est la seul implémentation du projet).
Il s’agit du contrôleur de notre application pour le modèle de donnée Actor. C’est cette classe qui va gérer l’API REST de notre application. Les contrôleurs au sens “Spring” sont repérés par une annotation spéciale
@RestController @RequestMapping("/api") public class ActorController {
L’annotation @RestController est une version spécialisée de l’annotation @Controller. Elle gère aussi l’ajout de l’annotation @ResponseBody aux méthodes portant les services du contrôleur.
Le controleur possède un attribut faisant une référence à la classe de service ActorService dont la résolution est faite grâce à l’annotation @Autowired:
@Autowired private ActorService actorService;
Sont ensuite définies les méthodes de la classe du contrôleur.
@RequestMapping(value = "/actors", method = RequestMethod.GET) public ResponseEntity<?> getAllActors(){ List<Actor> listActor = null; try { listActor = actorService.getAllActors(); } catch (Exception e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); } return ResponseEntity.status(HttpStatus.OK).body(listActor); }
Toutes les méthodes de notre API sont précédées de l’annotation @RequestMapping qui définira le chemin (fin de l’URL) et la méthode de la requête. Pour plus d’informations sur les différentes manières d’écrire une annotation @RequestMapping , rendez-vous sur http://www.baeldung.com/spring-requestmapping.
Remarque : Il possible de renvoyer directement des objets java en retour de fonction. Ces objets sont alors automatiquement sérialisés et renvoyés comme réponse à la requête associée. Nous avons choisi d’utiliser la classe ResponseEntity fournie par Spring qui permet un plus grand contrôle de la réponse HTTP (gestion des codes http, contenu de requête…).
Comme cela a déjà été évoqué, les éléments relatifs à la vue sont présents dans le répertoire main/src/resources/static. Il s’agit en fait d’un client html / javascript classique qui sera inclu au projet et déployé sur le serveur. Ce client utilisera l’API REST pour accéder aux données des acteurs via des requêtes Ajax.
Pour étudier ce fonctionnement, consulter les fichiers :
Remarque : L’API REST que propose notre serveur pourra facilement être utilisés par d’autre clients front. Il serait par exemple possible de l’utiliser via des applications mobiles (android, ios…).
Il existe des classes qui ont des fonctions définies :
Il existe une seule vue :
Tout est relié grâce à la magie de Spring et des annotations.
Dans le cadre du développement d’une API REST, il est nécessaire de pouvoir “émuler” les requêtes qu’appelleront les futurs clients front d’une application. Vous pouvez utiliser Postman.
Postman est un client équivalent à CURL qui fournit une interface graphique plus sympathique que Curl. Il permet de créer des collections, sauvegarde les requêtes jouées et peut même aller jusqu’à la création de scénarii de test.
Les liens ci-dessous vous aideront à voir comment il fonctionne :
Voici les données qui seront utilisées mais non mises en place dans la BD.
insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (1,'PENELOPE','GUINESS','2006-02-15 04:34:33'); insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (2,'NICK','WAHLBERG','2006-02-15 04:34:33'); insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (3,'ED','CHASE','2006-02-15 04:34:33'); insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (4,'JENNIFER','DAVIS','2006-02-15 04:34:33'); insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (5,'JOHNNY','LOLLOBRIGIDA','2006-02-15 04:34:33'); insert into `actor`(`actor_id`,`first_name`,`last_name`,`last_update`) values (6,'JENNIFER','LAWRENCE','2006-02-15 04:34:33');
Vous devez uniquement remplir les méthodes de tests de la classe suivante : ActorJdbcTest et ensuite lancez JUnit…
@RunWith(SpringRunner.class) @SpringBootTest(classes = ActorApplication.class) public class ActorJdbcTest { static ActorService actorSce; @Autowired @Qualifier("jdbcActorDAO") ActorDAO actorDAO; @BeforeClass public static void initActor() throws Exception { actorSce = new ActorService(); } @Test public void testFindOneOk() { // Instanciez un objet actor // Affectez lui un objet actor ayant l'identifiant 6 // Verifiez avec un assertEquals si le firstName est bien égale à Jennifer } @Test public void testFindOneKo() { // Instanciez un objet actor // Affectez lui un objet actor ayant l'identifiant 999 // Verifiez avec un assertNull que l'actor est bien null ! } @Test public void testFindOneBisOk() { // Instanciez un objet actor // Affectez lui un objet actor ayant l'identifiant 1 // Utilisez assertThat pour verifier que l'objet actor récupéré est bien une instance de la classe Actor } @Test public void testInsert() { // Instanciez 2 objets un actor et un actorNew à null // pour le premier actor, affectez lui un mock en utilisant la méthode privée createMock("Jean", "saisrien") // pour le second le actorNew, utilisez la méthode insertActor() en lui passant actor en paramètre // Utilisez assertTrue pour verifier que actorNew est différent de null } @Test public void testUpdate() { // Instanciez 2 objets un actor et un actorNew à null // Affectez les valeurs suivantes à actor : Jack Ouille id = 2 // pour le timestamp : actor.setLastUpdate(new Timestamp(System.currentTimeMillis())); // Initialisez actorNew en utilisant la méthode updateActor() en lui passant actor en paramètre. // ajoutez 2 assertions, l'une pour vérifier que actorNew n'est pas null // la seconde pour vérifier que "Jack" est bien le firstName de actorNew. } @Test public void testDelete() { // Instanciez un objet actor avec le constructeur sans argument // Déclarez une variable Long id avec la valeur 3 // Utilisez la méthode deleterActor() pour effacer l'actor avec l'id = 3 // essayez de récupérer un actor avec l'id = 3 } /** * Méthode privée de création d'un mock **/ private Actor createMock(String firstName, String lastName) { Actor mock = new Actor(); mock.setFirstName(firstName); mock.setLastName(lastName); mock.setId(new Long(0)); mock.setLastUpdate(new Timestamp(System.currentTimeMillis())); return mock; } }
Voici ce que vous devez obtenir comme résultats :