web-dev-qa-db-fra.com

Pourquoi les projections d'interface sont-elles beaucoup plus lentes que les projections de constructeur et les projections d'entité dans Spring Data JPA avec Hibernate?

Je me demandais quel type de projections devrais-je utiliser, j'ai donc fait un petit test, qui couvrait 5 types de projections (basé sur docs: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/# projections ):

1. Projection d'entité

Il s'agit simplement d'une findAll() standard fournie par le référentiel Spring Data. Rien d'extraordinaire ici.

Un service:

List<SampleEntity> projections = sampleRepository.findAll();

Entité:

@Entity
@Table(name = "SAMPLE_ENTITIES")
public class SampleEntity {
    @Id
    private Long id;
    private String name;
    private String city;
    private Integer age;
}

2. Projection du constructeur

Un service:

List<NameOnlyDTO> projections = sampleRepository.findAllNameOnlyConstructorProjection();

Dépôt:

@Query("select new path.to.dto.NameOnlyDTO(e.name) from SampleEntity e")
List<NameOnlyDTO> findAllNameOnlyConstructorProjection();

Objet de transfert de données:

@NoArgsConstructor
@AllArgsConstructor
public class NameOnlyDTO {
    private String name;
}

3. Projection d'interface

Un service:

List<NameOnly> projections = sampleRepository.findAllNameOnlyBy();

Dépôt:

List<NameOnly> findAllNameOnlyBy();

Interface:

public interface NameOnly {
    String getName();
}

4. Projection de tuple

Un service:

List<Tuple> projections = sampleRepository.findAllNameOnlyTupleProjection();

Dépôt:

@Query("select e.name as name from SampleEntity e")
List<Tuple> findAllNameOnlyTupleProjection();

5. Projection dynamique

Un service:

List<DynamicProjectionDTO> projections = sampleRepository.findAllBy(DynamicProjectionDTO.class);

Dépôt:

<T> List<T> findAllBy(Class<T> type);

Objet de transfert de données:

public class DynamicProjectionDTO {

    private String name;

    public DynamicProjectionDTO(String name) {
        this.name = name;
    }
}


Quelques informations supplémentaires:

Le projet a été construit à l'aide du plugin Gradle Spring Boot (version 2.0.4), qui utilise Spring 5.0.8 sous le capot. Base de données: H2 en mémoire.

Résultats:

Entity projections took 161.61 ms on average out of 100 iterations.
Constructor projections took 24.84 ms on average out of 100 iterations.
Interface projections took 252.26 ms on average out of 100 iterations.
Tuple projections took 21.41 ms on average out of 100 iterations.
Dynamic projections took 23.62 ms on average out of 100 iterations.
-----------------------------------------------------------------------
One iteration retrieved (from DB) and projected 100 000 objects.
-----------------------------------------------------------------------

Remarques:

Il est compréhensible que la récupération des entités prenne un certain temps. Hibernate suit ces objets pour les changements, le chargement paresseux, etc.

Les projections de constructeur sont très rapides et n'ont aucune limitation du côté DTO, mais nécessitent une création d'objet manuelle dans l'annotation @Query.

Les projections d'interface se sont avérées très lentes. Voir question.

Les projections de tuple étaient les plus rapides, mais ne sont pas les plus pratiques pour jouer avec. Ils ont besoin d'un alias dans JPQL et les données doivent être récupérées en appelant .get("name") au lieu de .getName().

Les projections dynamiques semblent assez cool et rapides, mais doivent avoir exactement un constructeur. Ni plus ni moins. Sinon, Spring Data lève une exception, car il ne sait pas laquelle utiliser (il prend les paramètres du constructeur pour déterminer quelles données extraire de la base de données).

Question:

Pourquoi les projections d'interface prennent plus de temps que la récupération d'entités? Chaque projection d'interface retournée est en fait un proxy. Est-ce si cher de créer ce proxy? Dans l'affirmative, cela ne va-t-il pas à l'encontre du but principal des projections (car elles sont censées être plus rapides que les entités)? D'autres projections ont l'air géniales. J'aimerais vraiment avoir un aperçu de cela. Je vous remercie.

EDIT: Voici le référentiel de test: https://github.com/aurora-software-ks/spring-boot-projections- test au cas où vous voudriez l'exécuter vous-même. C'est très simple à installer. Le fichier Lisez-moi contient tout ce que vous devez savoir.

16
Sikor

J'ai rencontré un comportement similaire avec une ancienne version de Spring Data et voici mon point de vue: https://blog.arnoldgalovics.com/how-much-projections-can-help/

J'ai eu une conversation avec Oliver Gierke (responsable des données de printemps) et il a apporté quelques améliorations (c'est pourquoi vous obtenez de si "bons" résultats :-)) mais en gros, il y aura toujours un coût à avoir des abstractions par rapport au codage manuel.

C'est un compromis comme tout le reste. D'une part, vous avez la flexibilité, un développement plus facile, moins de maintenance (espérons-le), d'autre part, vous obtenez un contrôle total, un modèle de requête un peu plus laid.

7
galovics