Gestion et optimisation de l'ordre des annotations dans MyBatis-Plus Generator

Lors de l'utilisation de MyBatis-Plus Generator pour automatiser la création de classes d'entités, un problème récurrent concerne l'ordre des annotations générées sur les champs de base de données. Par défaut, le moteur de génération produit souvent un agencement désordonné, ce qui nuit à la lisibilité du code source et ne respecte pas forcément les conventions de nommage internes d'une équipe de développement.

Analyse du comportement par défaut

Le comportement standard de MyBatis-Plus Generator s'appuie sur une logique de tri simpliste basée sur la longueur de la chaîne de caractères du nom de l'annotation. Voici un aperçu du mécanisme interne simplifié :

// Logique native de tri dans MyBatis-Plus
public List<AnnotationAttributes> getAnnotationAttributesList() {
    return this.annotationAttributesFunction != null ? 
        this.annotationAttributesFunction.apply(this.annotationAttributesList) : 
        this.annotationAttributesList.stream()
            .sorted(Comparator.comparingInt(attr -> attr.getDisplayName().length()))
            .collect(Collectors.toList());
}

Cette approche, triant par length(), place les annotations courtes avant les longues, ce qui est rarement le résultat souhaité lorsque l'on mélange des annotations de persistance (comme @TableId) et des annotations de documentation (comme @Schema ou Swagger).

Stratégie de résolution par personnalisation

La solution la plus élégante consiste à exploiter la méthode annotationAttributesFunction fournie par la configuration de stratégie (StrategyConfig). Cela permet d'injecter une logique de comparaison personnalisée.

1. Définition d'un référentiel de priorités

Il est recommandé de créer une structure de données définissant explicitement le poids de chaque type d'annotation.

public class ConfigOrdonnancement {
    private static final Map<String, Integer> RANG_ANNOTATIONS = new HashMap<>();

    static {
        RANG_ANNOTATIONS.put("TableId", 1);
        RANG_ANNOTATIONS.put("TableField", 2);
        RANG_ANNOTATIONS.put("TableLogic", 3);
        RANG_ANNOTATIONS.put("Version", 4);
        RANG_ANNOTATIONS.put("Schema", 5);
        RANG_ANNOTATIONS.put("ApiModelProperty", 6);
    }

    public static int obtenirPoids(String nomAffiche) {
        return RANG_ANNOTATIONS.entrySet().stream()
                .filter(entry -> nomAffiche.contains("@" + entry.getKey()))
                .map(Map.Entry::getValue)
                .findFirst()
                .orElse(99);
    }
}

2. Implémentatoin du moteur de tri

Une fois le référentiel établi, on peut construire la fonction de tri qui sera consommée par le générateur.

Function<List<? extends AnnotationAttributes>, List<AnnotationAttributes>> trieurPersonnalise = liste -> 
    liste.stream()
        .sorted(Comparator.comparingInt(attr -> ConfigOrdonnancement.obtenirPoids(attr.getDisplayName())))
        .collect(Collectors.toList());

Intégration dans FastAutoGenerator

Pour appliquer ce tri, il suffit de configurer l'entité dans le pipeline de génération de code.

public class GenerateurCode {
    public void executer() {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/db_exemple", "root", "password")
            .strategyConfig(builder -> builder
                .entityBuilder()
                .enableLombok()
                .annotationAttributesFunction(liste -> {
                    // Application de la logique de tri
                    return liste.stream()
                        .sorted(Comparator.comparingInt(a -> {
                            String nom = a.getDisplayName();
                            if (nom.contains("TableId")) return 1;
                            if (nom.contains("TableField")) return 2;
                            return 10;
                        }))
                        .collect(Collectors.toList());
                })
                .build()
            )
            .execute();
    }
}

Exemple de transformation

Avant optimisation, les annotations peuvent apparaître de manière aléatoire :

@ApiModelProperty("Identifiant unique")
@TableId(value = "id", type = IdType.AUTO)
private Long id;

Après application de la fonction de tri personnalisée, le code généré devient structuré et prévisible :

@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty("Identifiant unique")
private Long id;

Recommandations pour les environnements d'entreprise

Pour garantir la cohérence au sein d'une équipe, voici les meilleures pratiques :

  • Centralisation : Encapsulez la logique de tri dans un module Maven partagé ou un "Starter" interne pour que tous les microservices utilisent les mêmes règles de génération.
  • Utilisation des imports : Au lieu de filtrer sur le nom affiché (displayName), utilisez getImportPackages() pour identifier l'annotation de manière plus robuste par son nom de classe qualifié.
  • Flexibilité des templates : Si le tri des annotations ne suffit pas, envisagez de modifier les fichiers de template Velocity ou Freemarker (.vm ou .ftl) pour un contrôle total sur l'indentation et les sauts de ligne entre les annotations.

Étiquettes: MyBatis-Plus Java code-generation spring-boot persistence

Publié le 9 juillet à 09h26