Java Spring数据JPA投影数据库中的选定字段

Java Spring数据JPA投影数据库中的选定字段,java,spring-data-jpa,spring-data,projection,Java,Spring Data Jpa,Spring Data,Projection,我在测试SpringData1.10.4.RELEASE,遵循SpringDataDocs中的示例 我注意到一些问题,我有两个问题 首先,假设我有两个实体: @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName, lastName; @OneToOne private Address address; } @Entity public cla

我在测试SpringData1.10.4.RELEASE,遵循SpringDataDocs中的示例

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

首先,假设我有两个实体:

@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时

现在,如果我查看生成的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 = ?  
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”实体的投影仅选择“fistName”,这是100%正确的,因为在PersonLimited接口中,我只定义了“getFirstName”

但是对于“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 = ?  
SELECT person0_.firstName        AS firstName2_18_
FROM   person person0_ 

SELECT address0_.country    AS country4_13_0_
FROM   address address0_ 
WHERE  address0_.id = ? 
所以问题是,为什么它不为地址“实体”只选择“国家”字段?为什么需要选择所有字段?春天是虫子吗

  • 问题2:
对于与上述相同的投影

当我运行FindAllPersonProjectedForLimitedData时

现在,如果我查看生成的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 = ?  
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 = ? 
这里,个人和地址实体的投影都选择了所有字段,这是错误的,它应该只选择“firstName”和“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 = ?  
SELECT person0_.firstName        AS firstName2_18_
FROM   person person0_ 

SELECT address0_.country    AS country4_13_0_
FROM   address address0_ 
WHERE  address0_.id = ? 
这是正常行为吗?难道不应该只选择我们需要的字段吗


谢谢,

如果要将注释@Query与Spring数据投影一起使用,则必须使用字段别名,并且需要确保对与投影字段匹配的项目进行别名。以下代码适用于问题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);
}
interface PersonRepository扩展了crudepository{
@查询(“选择p.firstName作为firstName,选择p.address作为个人p的地址,其中p.firstName=?1”)
PersonLimited FindPersonByFirstName ProjectedForLimitedData(字符串名);
}
您可以使用的另一种选择是使用定义查询。只要有可能:

interface PersonRepository extends CrudRepository<Person, Long> {

  List<PersonLimited> findByFirstName(String firstName);
}
interface PersonRepository扩展了crudepository{
列出findByFirstName(字符串名);
}

我也遇到了同样的问题,当我将投影从接口更改为pojo类时,它正确地生成了SQL。

我不理解你的问题:它正选择它需要的东西。@Jenschauder,我编辑了我的问题。问题是,当使用“封闭投影”时,就像我正在使用的一样,JPA应该只选择我在投影“接口”中定义的字段。SpringData文档就是这样说的:封闭的投影公开了属性的子集,因此可以使用它们优化查询,以减少数据存储中所选字段,现在我了解您的问题。非常有趣。我会用标准的od方式做预测,意思是“从p个人中选择新的com.company.YourDto(p.firstName,p.address.country)”。与此接口方法相比,它在DTO中为您提供了更多的可能性。2年后,没有人回答它:(第一个解决方案不起作用,不幸的是,您必须调用构造函数,如:
“从Person p中选择new org.example.PersonLimited(p.firstName,p.address),其中p.firstName=?1”
@Andrés-Cuadros-Suárez是否可以在实体中有一个非必填字段。使用您的示例,@Query(“选择p.firstName作为Person p的firstName,其中p.firstName=?1”),如果查询中没有提到地址,那么对象中的地址字段应该是空的,我怎么能这样呢?@bekce第一个解决方案工作得很好。
PersonLimited
应该是一个接口。在我的情况下,我的投影有太多字段,无法使用
@query
中的构造函数手动构造。因此,我最后附加了一个predica例如,如果所有记录都保证有一个非空的
id
字段,你可以做
PersonLimited findallbydnotnull(Pageable Pageable)
。这可能有点老套,但还是比一个由20个字段组成的查询要好。你能在答案中插入一个例子吗?我也有同样的问题。