web-dev-qa-db-ja.com

Spring Data JPA ProjectionがDBから選択したフィールド

Spring Data Docsの例に従って、Spring Data 1.10.4.RELEASEをテストしていました http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections =

また、2つの質問がある問題に気付きました。

最初に、次の2つのエンティティがあるとします。

@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;
}
  • 質問1:

次の予測の場合:

interface PersonLimited {  

  String getFirstName(); 

  AddressLimited getAddress();
}

interface AddressLimited {  

  String getCountry(); 
}

実行するとfindPersonByFirstNameProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

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

期待どおりの結果を返します。

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

生成されたSQLを調べると、これが私が持っているものです:

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 = ?  

「Person」エンティティのプロジェクションは、「fistName」のみを選択しています。これは、PersonLimitedインターフェイスでは「getFirstName」のみを定義しているため、100%正しいです。

しかし、「Address」エンティティについては、すべてのフィールドを選択します。これは、AddressLimitedインターフェイスでは「getCountry」のみを定義しているため、「country」のみを選択する必要があるため間違っています。

生成されたクエリは次のようになります:

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 = ?  

質問は、なぜ住所「エンティティ」の「国」フィールドだけを選択しないのですか?すべてのフィールドを選択する必要があるのはなぜですか?それは春のバグですか?

  • 質問2:

上記と同じ投影法の場合、

実行するとfindAllPersonsProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

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

期待どおりの結果を返します。

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

生成されたSQLを調べると、これが私が持っているものです:

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 = ? 

ここでは、PersonエンティティとAddressエンティティの両方のプロジェクションは、間違っているすべてのフィールドを選択しています。「firstName」と「country」のみを選択する必要があります。

生成されたクエリは次のようになります:

SELECT person0_.firstName        AS firstName2_18_
FROM   person person0_ 

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

これは通常の動作ですか?必要なフィールドのみを選択するべきではありませんか?

おかげで、

21
arammal

Spring Data Projectionsで注釈@ Queryを使用する場合は、フィールドエイリアスを使用する必要があり、投影フィールドに一致するプロジェクトをエイリアスする必要があります。 。次のコードは質問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);
}

使用できる別の方法は、クエリを Property Expressions で定義することです。可能な限り:

interface PersonRepository extends CrudRepository<Person, Long> {

  List<PersonLimited> findByFirstName(String firstName);
}