vendredi 25 octobre 2024

[Spring boot] Problème rencontré avec `ddl-auto` par défaut dans les tests Hibernate

Lorsque nous exécutons nos tests Spring, nous avons remarqué qu’Hibernate **supprime** (drop) et **recrée** les tables à chaque exécution. Ce comportement entraîne la perte de données après chaque test.


Cela est dû à la configuration par défaut de Spring Boot : si une **base de données embarquée** (comme H2) est détectée et qu’aucun outil de gestion de schéma (par exemple Flyway ou Liquibase) n'est utilisé, alors `spring.jpa.hibernate.ddl-auto` passe automatiquement à `create-drop`. Ce mode initialise le schéma à chaque exécution, ce qui peut ne pas être souhaité en test ou en production.


**Documentation de référence :**

[Spring Boot Data Initialization](https://docs.spring.io/spring-boot/how-to/data-initialization.html)


---


### Solution : Configuration pour éviter la suppression du schéma dans les tests et en production

Ou bien utiliser CommandLineRunner pour peupler la BDD avant que spring ne soit prêt à recevoir des requêtes

1. **Configurer pour les tests** :

   Si vous souhaitez garder une base de données H2 pour les tests sans que les données soient perdues à chaque exécution, configurez `ddl-auto` comme suit :


   ```yaml

   spring:

     jpa:

       hibernate:

         ddl-auto: update  # Ne supprime rien, mais adapte la structure au besoin

     datasource:

       platform: h2

       url: jdbc:h2:./test-h2/test-db;DB_CLOSE_DELAY=-1;MODE=Oracle;TRACE_LEVEL_FILE=4;AUTO_SERVER=TRUE

       driver-class-name: org.h2.Driver

       username: sa

       password:

     h2:

       console:

         path: /manage/h2/console

         enabled: true

     jpa:

       properties:

         hibernate:

           dialect: org.hibernate.dialect.H2Dialect

   ```


   Cette configuration empêche Hibernate de supprimer les tables, mais permet toujours de mettre à jour leur structure si des changements sont détectés.


2. **Configurer pour la production** :

   En production, il est recommandé de **désactiver** `ddl-auto` ou de le configurer en mode `validate` pour empêcher Hibernate de modifier le schéma automatiquement. Utilisez des outils de migration de schéma comme **Flyway** ou **Liquibase** pour gérer les modifications.


   ```yaml

   spring:

     jpa:

       hibernate:

         ddl-auto: none

   ```


   Cette configuration garantit que Hibernate n’effectue aucune modification sur les tables SQL en production, laissant le contrôle du schéma aux outils de migration.


---


### Notes importantes


- Par défaut, si la propriété `spring.jpa.hibernate.ddl-auto` n’est pas explicitement définie, Spring Boot utilise `create-drop` pour les bases de données embarquées (comme H2).

- En production, il est **fortement recommandé** de définir `ddl-auto` sur `none` ou `validate` pour éviter les manipulations non intentionnelles du schéma.


En résumé, adaptez `ddl-auto` à l’environnement : utilisez `update` pour les tests si vous souhaitez conserver les données, et `none` ou `validate` en production pour un schéma sous contrôle.

jeudi 3 octobre 2024

[Spring] Le bean singleton dans Spring est-il thread-safe ?

 La réponse est non, il n'est pas thread-safe, et c'est pourquoi beaucoup de développeurs préfèrent utiliser l'injection par le constructeur plutôt que par le champ. De cette manière, ils peuvent définir leurs objets singleton comme immuables. Cela commence par rendre les attributs final.



La raison pour laquelle je veux que mes attributs soient immuables est de m'assurer que personne ne les modifie, car un singleton n'est pas synchronisé par Spring de quelque manière que ce soit. Il n'est même pas recommandé de le faire, ne changez jamais les attributs d'un objet singleton. Théoriquement, c'est possible, si pour une raison quelconque vous choisissez de le faire, assurez-vous de gérer la synchronisation vous-même. Vous pouvez utiliser le mot-clé synchronized en Java, vous pouvez utiliser des outils comme les sémaphores, ReadWriteLock, des barrières cycliques, etc.

Autre raccourcis avec Lombok :

@RestController

@RequiredArgsConstructor

public class DemoController {

    private final DemoService demoService;


    @GetMapping("/demo")

    public String getDemo() {

        return demoService.getDemoMessage();

    }

}