Java Spring数据JPA投影数据库中的选定字段
我在测试SpringData1.10.4.RELEASE,遵循SpringDataDocs中的示例 我注意到一些问题,我有两个问题 首先,假设我有两个实体: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
@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:
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个字段组成的查询要好。你能在答案中插入一个例子吗?我也有同样的问题。