DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Uncategorized»Comment utiliser au mieux les enregistrements Java en tant que DTO dans Spring Boot 3
    Uncategorized

    Comment utiliser au mieux les enregistrements Java en tant que DTO dans Spring Boot 3

    février 25, 2023
    Comment utiliser au mieux les enregistrements Java en tant que DTO dans Spring Boot 3
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Avec les versions Spring 6 et Spring Boot 3, Java 17+ est devenu la version de base du framework. C’est donc le moment idéal pour commencer à utiliser des enregistrements Java compacts en tant qu’objets de transfert de données (DTO) pour divers appels de base de données et d’API.

    Que vous préfériez lire ou regarder, passons en revue quelques approches pour utiliser les enregistrements Java en tant que DTO qui s’appliquent à Spring Boot 3 avec Hibernate 6 comme fournisseur de persistance.

    Exemple de base de données

    Suivez ces instructions si vous souhaitez installer l’exemple de base de données et expérimenter vous-même. Sinon, n’hésitez pas à sauter cette section :

    1. Téléchargez le jeu de données de la base de données Chinook (magasin de musique) pour la syntaxe PostgreSQL.

    2. Démarrez une instance de YugabyteDB, une base de données distribuée compatible PostgreSQL, dans Docker :

    mkdir ~/yb_docker_data
    
    docker network create custom-network
    
    docker run -d --name yugabytedb_node1 --net custom-network \
      -p 7001:7000 -p 9000:9000 -p 5433:5433 \
      -v ~/yb_docker_data/node1:/home/yugabyte/yb_data --restart unless-stopped \
      yugabytedb/yugabyte:latest \
      bin/yugabyted start \
      --base_dir=/home/yugabyte/yb_data --daemon=false

    3. Créez le chinook base de données dans YugabyteDB :

    createdb -h 127.0.0.1 -p 5433 -U yugabyte -E UTF8 chinook

    4. Chargez l’exemple d’ensemble de données :

    psql -h 127.0.0.1 -p 5433 -U yugabyte -f Chinook_PostgreSql_utf8.sql -d chinook

    Ensuite, créez un exemple d’application Spring Boot 3 :

    1. Générez un modèle d’application en utilisant Spring Boot 3+ et Java 17+ avec Spring Data JPA comme dépendance.

    2. Ajoutez le pilote PostgreSQL au pom.xml déposer:

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.5.4</version>
    </dependency>

    3. Fournissez les paramètres de connectivité YugabyteDB dans le application.properties déposer:

    spring.datasource.url = jdbc:postgresql://127.0.0.1:5433/chinook
    spring.datasource.username = yugabyte
    spring.datasource.password = yugabyte

    Tout est prêt ! Maintenant, vous êtes prêt à suivre le reste du guide.

    Modèle de données

    La base de données Chinook est livrée avec de nombreuses relations, mais deux tables suffiront amplement pour montrer comment utiliser les enregistrements Java en tant que DTO.

    Le premier tableau est Tracket ci-dessous une définition d’une classe d’entité JPA correspondante :

    @Entity
    public class Track {
        @Id
        private Integer trackId;
    
        @Column(nullable = false)
        private String name;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "album_id")
        private Album album;
    
        @Column(nullable = false)
        private Integer mediaTypeId;
    
        private Integer genreId;
    
        private String composer;
    
        @Column(nullable = false)
        private Integer milliseconds;
    
        private Integer bytes;
    
        @Column(nullable = false)
        private BigDecimal unitPrice;
    
        // Getters and setters are omitted
    }

    Le deuxième tableau est Album et a la classe d’entité suivante :

    @Entity
    public class Album {
        @Id
        private Integer albumId;
    
        @Column(nullable = false)
        private String title;
    
        @Column(nullable = false)
        private Integer artistId;
    
        // Getters and setters are omitted
    }

    En plus des classes d’entités, créez un enregistrement Java nommé TrackRecord qui stocke des informations courtes mais descriptives sur la chanson :

    public record TrackRecord(String name, String album, String composer) {}

    Approche naïve

    Imaginez que vous deviez implémenter un point de terminaison REST qui renvoie une courte description de la chanson. L’API doit fournir les noms des chansons et des albums, ainsi que le nom de l’auteur.

    Le créé précédemment TrackRecord classe peut contenir les informations requises. Alors, créons un enregistrement en utilisant l’approche naïve qui obtient les données via JPA Entity Des classes:

    1. Ajoutez le référentiel JPA suivant :

    public interface TrackRepository extends JpaRepository<Track, Integer> {
    }

    2. Ajoutez la méthode de niveau de service de Spring Boot qui crée un TrackRecord instance de la Track classe d’entité. Ce dernier est récupéré via le TrackRepository exemple:

    @Transactional(readOnly = true)
    public TrackRecord getTrackRecord(Integer trackId) {
        Track track = repository.findById(trackId).get();
    
        TrackRecord trackRecord = new TrackRecord(
          track.getName(),
          track.getAlbum().getTitle(),
          track.getComposer());
    
        return trackRecord;
    }

    La solution semble simple et compacte, mais elle est très inefficace car Hibernate doit d’abord instancier deux entités : Track et Album (voir le track.getAlbum().getTitle()). Pour cela, il génère deux requêtes SQL qui sollicitent toutes les colonnes des tables correspondantes de la base de données :

    Hibernate: 
        select
            t1_0.track_id,
            t1_0.album_id,
            t1_0.bytes,
            t1_0.composer,
            t1_0.genre_id,
            t1_0.media_type_id,
            t1_0.milliseconds,
            t1_0.name,
            t1_0.unit_price 
        from
            track t1_0 
        where
            t1_0.track_id=?
    Hibernate: 
        select
            a1_0.album_id,
            a1_0.artist_id,
            a1_0.title 
        from
            album a1_0 
        where
            a1_0.album_id=?

    Hibernate sélectionne 12 colonnes sur deux tables, mais TrackRecord n’a besoin que de trois colonnes ! C’est un gaspillage de mémoire, de calcul et de ressources réseau, en particulier si vous utilisez des bases de données distribuées comme YugabyteDB qui disperse les données sur plusieurs nœuds de cluster.

    TupleTransformer

    L’approche naïve peut être facilement corrigée si vous interrogez uniquement les enregistrements requis par l’API, puis transformez un ensemble de résultats de requête en un enregistrement Java respectif.

    Le module Spring Data de Spring Boot 3 s’appuie sur Hibernate 6. Cette version d’Hibernate divise le ResultTransformer interface en deux interfaces : TupleTransformer et ResultListTransformer.

    Le TupleTransformer classe prend en charge les enregistrements Java, donc, l’implémentation du public TrackRecord getTrackRecord(Integer trackId) peut être optimisé de cette façon :

    @Transactional(readOnly = true)
    public TrackRecord getTrackRecord(Integer trackId) {
    	org.hibernate.query.Query<TrackRecord> query = entityManager.createQuery(
          """
          SELECT t.name, a.title, t.composer
          FROM Track t
          JOIN Album a ON t.album.albumId=a.albumId
          WHERE t.trackId=:id
          """).
          setParameter("id", trackId).
          unwrap(org.hibernate.query.Query.class);
    
          TrackRecord trackRecord = query.setTupleTransformer((tuple, aliases) -> {
            return new TrackRecord(
              (String) tuple[0],
              (String) tuple[1],
              (String) tuple[2]);
          }).getSingleResult();
    
        return trackRecord;
    }

    • entityManager.createQuery(...) – Crée une requête JPA qui demande trois colonnes nécessaires pour le TrackRecord classe.
    • query.setTupleTransformer(...) – Le TupleTransformer prend en charge les enregistrements Java, ce qui signifie qu’un TrackRecord instance peut être créée dans l’implémentation du transformateur.

    Cette approche est plus efficace que la précédente car vous n’avez plus besoin de créer des classes d’entités et pouvez facilement construire un enregistrement Java avec le TupleTransformer. De plus, Hibernate génère une seule requête SQL qui renvoie uniquement les colonnes requises :

    Hibernate: 
        select
            t1_0.name,
            a1_0.title,
            t1_0.composer 
        from
            track t1_0 
        join
            album a1_0 
                on t1_0.album_id=a1_0.album_id 
        where
            t1_0.track_id=?

    Cependant, il y a un inconvénient très visible à cette approche : la mise en œuvre de la public TrackRecord getTrackRecord(Integer trackId) la méthode est devenue plus longue et plus verbeuse.

    Enregistrement Java dans la requête JPA

    Il existe plusieurs façons de raccourcir l’implémentation précédente. L’une consiste à instancier une instance d’enregistrement Java dans une requête JPA.

    Tout d’abord, étendre la mise en œuvre de la TrackRepository interface avec une requête personnalisée qui crée un TrackRecord instance à partir des colonnes de base de données demandées :

    public interface TrackRepository extends JpaRepository<Track, Integer> {
    
    @Query("""
             SELECT new com.my.springboot.app.TrackRecord(t.name, a.title, t.composer)
             FROM Track t
             JOIN Album a ON t.album.albumId=a.albumId
             WHERE t.trackId=:id
             """)
    TrackRecord findTrackRecord(@Param("id") Integer trackId);
    }

    Ensuite, mettez à jour la mise en œuvre du TrackRecord getTrackRecord(Integer trackId) méthode de cette façon :

    @Transactional(readOnly = true)
    public TrackRecord getTrackRecord(Integer trackId) {
         return repository.findTrackRecord(trackId);
    }

    Ainsi, la mise en œuvre de la méthode est devenue un one-liner qui obtient un TrackRecord instance directement depuis le référentiel JPA : le plus simple possible.

    Mais ce n’est pas tout. Il y a encore un petit problème. La requête JPA qui construit un enregistrement Java nécessite que vous fournissiez un nom de package complet pour le TrackRecord classe:

    SELECT new com.my.springboot.app.TrackRecord(t.name, a.title, t.composer)...

    Trouvons un moyen de contourner cette exigence. Idéalement, l’enregistrement Java doit être instancié sans le nom du package :

    SELECT new TrackRecord(t.name, a.title, t.composer)...

    Utilitaires d’hypersistence

    La bibliothèque Hypersistence Utils est livrée avec de nombreux avantages pour Spring et Hibernate. Une fonctionnalité vous permet de créer une instance d’enregistrement Java dans une requête JPA sans le nom du package.

    Activons la bibliothèque et cette fonctionnalité liée aux enregistrements Java dans l’application Spring Boot :

    1. Ajoutez l’artefact Maven de la bibliothèque pour Hibernate 6.

    2. Créez une coutume IntegratorProvider qui enregistre TrackRecord classe avec Hibernate :

    public class ClassImportIntegratorProvider implements IntegratorProvider {
        @Override
        public List<Integrator> getIntegrators() {
            return List.of(new ClassImportIntegrator(List.of(TrackRecord.class)));
        }
    }
    

    3. Mettez à jour le application.properties fichier en ajoutant cette coutume IntegratorProvider:

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.