背景
假设我们存在一个User实体,同时有一个接口要查询id、firstName、lastName,我们如何实现呢
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private Integer age;
private String idCard;
private Date birthday;
@Temporal(TemporalType.DATE)
private Date createTime;
private Date updateTime;
}
public interface UserRepository extends JpaRepository<User,Long>{
List<User> findByAge(Integer age);
}
最直接的做法就是调用findByAge
方法,然后循环将List中的User转换为返回对象,但这有两个不好的地方
- 该查询会将User的所有属性都查询出来,我们只需要3个字段
- 会有一层for循环对象转换的过程,DO-VO
Projection
使用Projection能解决以上两个问题,Projection并不是一个注解开关,而是一种写法。Spring Data允许自定义的返回类型,可以有选择地检索托需要的字段,避免查询所有字段。我们可以将返回类型定义为接口所需要的字段,就可以避免一次没必要的for循环对象转换。
public interface UserRepository extends JpaRepository<User,Long> {
List<UserInfo> findByAge(Integer age);
interface UserInfo{
Long getId();
String getFirstName();
String getLastName();
}
}
可以看到现在将findByAge
的返回结果范型改为UserInfo,UserInfo是一个接口,只包含方法声明,这样JPA就知道查询时只需要UserInfo中的3个字段了
接口的调用如下
@GetMapping(value = "/users")
public HttpEntity<List<UserRepository.UserInfo>> queryUserInfo(Integer age){
return ResponseEntity.ok(userRepository.findByAge(age));
}
控制台日志
Hibernate: select user0_.id as col_0_0_, user0_.first_name as col_1_0_, user0_.last_name as col_2_0_ from user user0_ where user0_.age=?
如果想把id改成userId返回,需要用到@Value
填充值,同时给getId加上@JsonIgnore
interface UserInfo{
@JsonIgnore
Long getId();
@Value("#{target.id}")
String getUserid();
String getFirstName();
String getLastName();
}