web-dev-qa-db-fra.com

Champs sélectionnés par Spring Data JPA Projection dans la base de données

Je testais Spring Data 1.10.4.RELEASE, en suivant l'exemple dans Spring Data Docs http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections =

et j'ai remarqué quelques problèmes pour lesquels j'ai 2 questions.

Supposons d'abord que j'ai ces 2 entités:

@Entity
public class Person {

  @Id @GeneratedValue
  private Long id;
  private String firstName, lastName;

  @OneToOne
  private Address address;
}

@Entity
public class Address {

  @Id @GeneratedValue
  private Long id;
  private String street, state, country;
}
  • Question 1:

pour les projections suivantes:

interface PersonLimited {  

  String getFirstName(); 

  AddressLimited getAddress();
}

interface AddressLimited {  

  String getCountry(); 
}

quand j'exécute findPersonByFirstNameProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

  @Query("select p from Person p where p.firstName = ?1")
  PersonLimited findPersonByFirstNameProjectedForLimitedData(String firstName);
}

il renvoie exactement ce qui était attendu:

{
    firstName: 'Homer',
    address: {
        country: 'USA'
    }
}

maintenant si je regarde dans le SQL généré, voici ce que j'ai:

SELECT person0_.firstName      AS col_0_0_, 
       address1_.id            AS id1_13_, 
       address1_.street        AS street2_13_, 
       address1_.state         AS state3_13_, 
       address1_.country       AS country4_13_
FROM   person person0_ 
       LEFT OUTER JOIN address address1_ 
                    ON person0_.addressId = address1_.id 
WHERE  person0_.firstName = ?  

La projection pour l'entité "Person" sélectionne uniquement "fistName", ce qui est 100% correct parce que dans l'interface PersonLimited, je n'ai défini que "getFirstName".

Mais pour l'entité "Address", il sélectionne tous les champs, ce qui est faux car dans l'interface AddressLimited, je n'ai défini que "getCountry", il ne doit sélectionner que "country".

La requête générée doit ressembler à:

SELECT person0_.firstName      AS col_0_0_, 
       address1_.country       AS country4_13_
FROM   person person0_ 
       LEFT OUTER JOIN address address1_ 
                    ON person0_.addressId = address1_.id 
WHERE  person0_.firstName = ?  

la question est donc de savoir pourquoi il ne sélectionne pas uniquement le champ "pays" pour l'adresse "entité"? pourquoi doit-il sélectionner tous les champs? est-ce un bug au printemps?

  • Question 2:

pour la même projection que ci-dessus,

quand j'exécute findAllPersonsProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

  @Query("select p from Person p")
  List<PersonLimited> findAllPersonsProjectedForLimitedData();
}

il renvoie exactement ce qui était attendu:

[
     {
        firstName: 'Homer',
        address: {
            country: 'USA'
        }
     },
     {
        firstName: 'Maggie',
        address: {
            country: 'USA'
        }
     }
]

maintenant si je regarde dans le SQL généré, voici ce que j'ai:

SELECT person0_.id                 AS id1_18_, 
       person0_.firstName          AS firstName2_18_, 
       person0_.lastName           AS lastName3_18_, 
       person0_.addressid          AS company4_18_
FROM   person person0_ 

SELECT address0_.id         AS id1_13_0_, 
       address0_.street     AS street2_13_0_, 
       address0_.state      AS state3_13_0_, 
       address0_.country    AS country4_13_0_
FROM   address address0_ 
WHERE  address0_.id = ? 

ici, la projection pour les entités Personne et Adresse sélectionne tous les champs qui sont faux, elle ne doit sélectionner que "prénom" et "pays".

La requête générée doit ressembler à:

SELECT person0_.firstName        AS firstName2_18_
FROM   person person0_ 

SELECT address0_.country    AS country4_13_0_
FROM   address address0_ 
WHERE  address0_.id = ? 

est-ce le comportement normal, ne devrait-on pas sélectionner uniquement les champs dont nous avons besoin?

Merci,

21
arammal

Si vous souhaitez utiliser l'annotation @ Query avec Spring Data Projections, vous devez utiliser l'alias de champ et vous devez vous assurer d'alias les projets correspondant aux champs de projection . Le code suivant devrait fonctionner pour la question 1:

interface PersonRepository extends CrudRepository<Person, Long> {

  @Query("select p.firstName as firstname, p.address as address from Person p where p.firstName = ?1")
  PersonLimited findPersonByFirstNameProjectedForLimitedData(String firstName);
}

Une autre alternative que vous pouvez utiliser est de définir vos requêtes avec Property Expressions . chaque fois que cela est possible:

interface PersonRepository extends CrudRepository<Person, Long> {

  List<PersonLimited> findByFirstName(String firstName);
}