Utilisation de Liquibase pour gérer la structure de la base de données dans une application Spring Boot. 2e partie

Dans la partie précédente, nous avons compris les fonctionnalités de base de Liquibase et écrit un exemple de base d'une application de démarrage Spring qui utilise Liquibase pour initialiser la base de données. Le code d'application de base complet peut être vu ici sur GitHub . Dans cet article, nous parlerons du plugin liquibase-maven et des fonctionnalités supplémentaires qu'il nous offre pour la version de la structure de la base de données. Commençons par créer automatiquement des scripts à l' aide de la fonction de comparaison .

Supposons que nous devions apporter des modifications à la structure de notre base de données. Par exemple, nous voulons que l' e - mail ne soit pas nul . Bien sûr, pour une si petite modification, il serait possible d'ajuster manuellement le code et les scripts, mais qu'en est-il s'il y a plus de modifications? Dans ce cas, la possibilité de comparer la base de données intégrée à Liquibase nous sera utile. Une caractéristique intéressante est que vous pouvez comparer non seulement deux bases de données, mais aussi une base de données avec un ensemble d'entités JPA dans notre application. C'est exactement ce que nous allons faire maintenant!

Créer un script avec des modifications à l'aide de liquibase-diff


Dans la section plugins du fichier pom.xml , nous ajoutons cette conception plutôt compliquée. Il s'agit du plugin liquibase-maven , auquel une dépendance est connectée pour analyser les entités en veille prolongée et travailler avec les fichiers YAML. Le plugin nous aidera à générer automatiquement des scripts de base de données en comparant les structures de deux bases de données ou même en comparant la structure de données dans une base de données et un ensemble d'entités hiberante dans notre application ( liquibase-hibernate5 est ajouté pour cela).

<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.8.1</version> <configuration> <propertyFile>${project.build.outputDirectory}/liquibase-maven-plugin.properties</propertyFile> <systemProperties> <user.name>your_liquibase_username</user.name> </systemProperties> <logging>info</logging> </configuration> <dependencies> <dependency> <groupId>org.liquibase.ext</groupId> <artifactId>liquibase-hibernate5</artifactId> <version>3.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.2.0.RELEASE</version> </dependency> </dependencies> </plugin> 

Faites attention au paramètre user.name . Il est facultatif, mais sans lui, Liquibase indiquera dans les scripts créés le nom de l'utilisateur actuel du système d'exploitation sous lequel le plugin est lancé.

Les paramètres du plugin peuvent être écrits directement dans pom.xml ou passés en tant que paramètres de ligne de commande lors de l'appel de maven, mais je préfère l'option avec un fichier liquibase-maven-plugin.properties distinct. Son contenu ressemblera à ceci.

 changeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-master.yaml url= jdbc:mysql://localhost:3306/geek_db?createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username= dbusername password= dbpassword driver= com.mysql.cj.jdbc.Driver referenceUrl= hibernate:spring:ru.usharik.liquibase.demo.persist.model?dialect=org.hibernate.dialect.MySQLDialect diffChangeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-@timestamp@.yaml ignoreClasspathPrefix= true 
Il convient de prêter attention aux paramètres url et referenceUrl . Le script qui créera la base de données après comparaison sera la différence entre la base utilisant le lien referenceUrl et la base utilisant le lien url . Si vous exécutez ultérieurement ce script sur la base à l'aide du lien URL , il deviendra le même que celui situé sur le lien referenceUrl. Une attention particulière doit être portée au lien referenceUrl. Comme vous pouvez le voir, il ne fait pas référence à la base de données, mais au package de notre application dans lequel se trouvent les classes d'entités. Grâce à cela, nous pouvons maintenant trouver un script qui ajoutera à la base de données les modifications apportées au code.

Nous devons maintenant configurer le plug-in maven-resource pour remplacer les espaces réservés dans le fichier de paramètres, tels que @project.basedir@ et @timestamp@ . Pour ce faire, ajoutez la section ressources du formulaire suivant à la section build

 <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <includes> <include>**/*.*</include> </includes> </resource> </resources> 

Soit dit en passant, Spring boot modifie le format standard pour les espaces réservés remplis à l'aide de maven-resource-plugin de ${smth} à @smth@ . Le fait est que les espaces réservés de @smth@ dans Spring Boot sont utilisés pour remplacer les variables d'environnement et les paramètres de l'application Spring Boot elle-même.

De plus, nous modifions légèrement la section des propriétés dans pom.xml pour affecter la valeur à la variable d' horodatage au format dont nous avons besoin. Hélas, le format standard peut contenir des caractères interdits dans les noms de fichiers pour certains systèmes d'exploitation.

 <properties> <java.version>1.8</java.version> <timestamp>${maven.build.timestamp}</timestamp> <maven.build.timestamp.format>yyyyMMdd-HHmmssSSS</maven.build.timestamp.format> </properties> 

Modifions maintenant le champ e-mail dans la classe Utilisateur

  @Column(name = "email", nullable = false) private String email; 

Enfin, exécutez la commande build en utilisant liquibase-maven-plugin pour comparaison.

 mvn clean install liquibase:diff -DskipTests=true 

Dans ce cas, nous devons reconstruire complètement le projet, car le plugin (liquibase: diff) utilisera non pas des sources compilées, mais des fichiers de classe d'entité compilés à partir du dossier cible pour l'analyse.

Si tout est fait correctement, après une exécution réussie de la commande dans le dossier resources / db / changelog , vous verrez un fichier avec le nom db.changelog-20190723-100748666.yaml . Étant donné que nous utilisons la date et l'heure actuelles dans le nom du fichier, à chaque lancement, nous aurons un nouveau fichier, ce qui est très pratique. Si vous avez déjà créé une base de données avec la structure de table correspondant à la leçon précédente, le contenu du fichier devrait être comme ceci.

 databaseChangeLog: - changeSet: id: 1563876485764-1 author: your_liquibase_username (generated) changes: - addNotNullConstraint: columnDataType: varchar(255) columnName: email tableName: users 
Comme vous pouvez le voir, ce script apporte exactement la modification qui a été apportée au code. Comme exercice, je vous recommande d'exécuter ce script sur une base de données vide et de regarder le résultat.

Ensuite, nous pouvons simplement copier le changeSet de ce fichier vers db.changelog-master.yaml ou nous pouvons y connecter ce fichier avec une instruction

  - include: file: db.changelog-20190723-100748666.yaml relativeToChangelogFile: true 

Dans ce fichier, vous devez également spécifier logicalFilePath: db/changelog/db.changelog-20190723-100748666.yaml , de la même manière que cela a été fait dans db.changelog-master.yaml .
Cela aidera à faire face à certains problèmes qui sont possibles lors de l'utilisation du bean liquibase intégré et du plugin liquibase-maven dans l'application. Après cela, redémarrez l'application ou exécutez la commande:

 mvn liquibase:update 

Essayons d'apporter des modifications plus complexes au code. Par exemple, ajoutez une table de rĂ´le qui aura une relation plusieurs-Ă -plusieurs avec la table utilisateur.

 @Entity @Table(name = "roles") public class Role implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name", unique = true, nullable = false) private String name; @ManyToMany(mappedBy = "roles") private Set<User> users; //  , ,  } 

Et dans le tableau des utilisateurs, nous ajoutons
  @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles; 

Après avoir commencé la comparaison, nous obtenons un fichier avec le contenu suivant
 databaseChangeLog: - changeSet: id: 1563877765929-1 author: your_liquibase_username (generated) changes: - createTable: columns: - column: autoIncrement: true constraints: primaryKey: true primaryKeyName: rolesPK name: id type: BIGINT - column: constraints: nullable: false name: name type: VARCHAR(255) tableName: roles - changeSet: id: 1563877765929-2 author: your_liquibase_username (generated) changes: - createTable: columns: - column: constraints: nullable: false name: user_id type: BIGINT - column: constraints: nullable: false name: role_id type: BIGINT tableName: users_roles - changeSet: id: 1563877765929-3 author: your_liquibase_username (generated) changes: - addPrimaryKey: columnNames: user_id, role_id tableName: users_roles - changeSet: id: 1563877765929-4 author: your_liquibase_username (generated) changes: - addUniqueConstraint: columnNames: name constraintName: UC_ROLESNAME_COL tableName: roles - changeSet: id: 1563877765929-5 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: user_id baseTableName: users_roles constraintName: FK2o0jvgh89lemvvo17cbqvdxaa deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: users - changeSet: id: 1563877765929-6 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: role_id baseTableName: users_roles constraintName: FKj6m8fwv7oqv74fcehir1a9ffy deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: roles 

Nous pouvons également facilement ajouter ce fichier à des scripts de travail.

Annuler les modifications


Voyons maintenant comment annuler les modifications apportées. Pour une raison quelconque, les identifiants que nous avons spécifiés dans changeSets ne peuvent pas être utilisés pour y revenir. Il existe trois options, spécifiez un point de restauration

  • Ă  travers le nombre de changeSets comptant Ă  partir du courant
  • jusqu'Ă  la date du changement
  • via une balise (dĂ©finie Ă  l'aide de changeSet d'un type spĂ©cial)

La balise est définie comme suit.

  - changeSet: id: some_uniqui_id author: liquibase_user_name changes: - tagDatabase: tag: db_tag 

Eh bien, les commandes pour les trois façons énumérées de faire une restauration

 mvn liquibase:rollback -Dliquibase.rollbackTag=db_tag mvn liquibase:rollback -Dliquibase.rollbackCount=1 mvn liquibase:rollback "-Dliquibase.rollbackDate=Jun 03, 2017" 

Et enfin, quelques matériaux supplémentaires

  1. Code pour cet article
  2. À propos de la restauration dans Liquibase
  3. À propos de la migration avec Liquibase
  4. Liquibase sur github
  5. Très bon article sur les différentes approches de versioning d'une base de données

Bien sûr, je serai très heureux de tout commentaire, ajout, clarification, etc.

Source: https://habr.com/ru/post/fr460907/


All Articles