Java Spring Data JPA Projection 从数据库中选择的字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40194614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:29:18  来源:igfitidea点击:

Spring Data JPA Projection selected fields from the DB

javaspring-data-jpaspring-dataprojection

提问by arammal

I was testing Spring Data 1.10.4.RELEASE, following the example in Spring Data Docs http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

我正在测试 Spring Data 1.10.4.RELEASE,遵循 Spring Data Docs http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections 中的示例

and I noticed some issues for which I have 2 questions.

我注意到一些问题,我有两个问题。

First let's suppose I have these 2 entities:

首先让我们假设我有这两个实体:

@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:
  • 问题 1:

for the following projections:

对于以下预测:

interface PersonLimited {  

  String getFirstName(); 

  AddressLimited getAddress();
}

interface AddressLimited {  

  String getCountry(); 
}

when I run findPersonByFirstNameProjectedForLimitedData

当我运行findPersonByFirstNameProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

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

it returns exactly what expected :

它返回的正是预期的:

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

now if I look into the generated SQL, this is what I have:

现在,如果我查看生成的 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 = ?  

The projection for the "Person" entity is selecting only "fistName", which is 100% correct because in the PersonLimited interface I've only defined "getFirstName".

“Person”实体的投影仅选择“fistName”,这是 100% 正确的,因为在 PersonLimited 接口中我只定义了“getFirstName”。

But for the "Address" entity, it selects all the fields, which is wrong because in the AddressLimited interface I've only defined "getCountry", It should only select "country".

但是对于“Address”实体,它选择了所有字段,这是错误的,因为在 AddressLimited 接口中我只定义了“getCountry”,它应该只选择“country”。

The generated query should be something like:

生成的查询应该是这样的:

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

so the question is, why it is not selecting only the "country" field for the Address "entity"? why it needs to select all the fields? is it a bug in Spring?

所以问题是,为什么它不只为地址“实体”选择“国家”字段?为什么它需要选择所有字段?这是Spring的错误吗?

  • Question 2:
  • 问题2:

for the same projection as above,

对于与上述相同的投影,

when I run findAllPersonsProjectedForLimitedData

当我运行findAllPersonsProjectedForLimitedData

interface PersonRepository extends CrudRepository<Person, Long> {

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

it returns exactly what expected :

它返回的正是预期的:

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

now if I look into the generated SQL, this is what I have:

现在,如果我查看生成的 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 = ? 

here, the projection for both the Person and the Address entities is selecting all the fields which is wrong, it should only select "firstName" and "country".

在这里,Person 和 Address 实体的投影选择了所有错误的字段,它应该只选择“firstName”和“country”。

The generated query should be something like:

生成的查询应该是这样的:

SELECT person0_.firstName        AS firstName2_18_
FROM   person person0_ 

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

is this the normal behavior, shouldn't select only the fields that we need?

这是正常行为,不应该只选择我们需要的字段吗?

Thanks,

谢谢,

回答by Andrés Cuadros Suárez

If you want use the annotation @Querywith Spring Data Projections you have to use field alias and you need to make sure you alias the projects matching the projection fields. The following code should work for question 1:

如果要将注解@Query与 Spring Data Projections 一起使用,则必须使用字段别名,并且需要确保为与投影字段匹配的项目设置别名。以下代码应该适用于问题 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);
}

Another alternative that you can use is define your queries with Property Expressions. whenever is possible:

您可以使用的另一种替代方法是使用Property Expressions定义您的查询。只要有可能:

interface PersonRepository extends CrudRepository<Person, Long> {

  List<PersonLimited> findByFirstName(String firstName);
}