Spring Data JPA: Projection

学习的文章资料:

  • Spring Data JPA进阶(二):Projections

  • JPA 自定义返回字段映射

  • spring-data-examples/tree/master/jpa


在JPA查询中,全部都是默认返回对象的所有属性,若:

  • 你只需要对象的几个属性
  • 你只不需要对象的某几个属性,剩下的属性全部要

那么,返回对象的全部属性就变成了负担。

解决方案

方案(一) 使用@Query注解
@Query("select u.name from User u where u.id=?1")
String getNameById(int id);

如果想返回对象,则需要根据自己的需求创建新的对象。

// 对应到表的Entity
@Data
public class User {
    private int id;
    private String name;
    private Integer age;
    private String tel;
    private String email;


    // 根据自己需要创建对应的构造函数
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

那么,可以这样做

@Query("select new User(u.name, u.age) from User u where u.id=?1")
User getNameAndAgeFromId(int id);

以上方式,只适合:只需要对象的几个属性,如果需要的属性过多,那么写在@Query的语句也会越长,不利于阅读和维护。

方案(二) 使用JPA提供的Projection投影功能

JPA提供了一种声明方式来解决,即声明一个接口类,然后直接使用这个接口类接受返回的数据即可。这个接口发挥了一个类似于“视图”的作用。

import org.springframework.beans.factory.annotation.Value;
// 注意这个@Value注解是用的Spring的注解.
//这里使用的是SpEL的语法,target即原始类型的对象。在Java 8以后的版本,除了使用 //@Value注解,你还可以使用接口的default方法:

public interface UserProjection {
    @Value("#{target.name + ' ' + target.email}")  // Projection 可以进行字段重组
    String getNameAndEmail();

    String getName();

    String getAge();
// default 方法实例
  default String getFullName() {
    return getName().concat(" ").concat(getAge());
  }
}

然后可以在自定义的Repository里面将这个接口类型做为返回值。

public interface UserRepository extends JpaRepository {

  Collection findByName(String name);
}
使用DTO

除了使用上面的接口外,我们也可以声明DTO,或者如果是直接作为返回值,我们也可以称之为VO。

class UserOnly {

  private final String name, email;

  UserOnly(String name, String lastname) {

    this.name = name;
    this.email = email;
  }

  String getName() {
    return this.name;
  }

  String getEmail() {
    return this.email;
  }

  // equals(…) and hashCode() implementations
}

使用Lombok 简化代码:

import lombok.Value;

@Value
class UserOnly {
  private final String name, email;
}

Lombok提供了对DTO的注解@Value。注意这个注解跟Spring的@Value注解不是同一个,自己引包的时候要注意一下。上面一段代码简化之后是这个样子

动态的Projection

可不可以有很多Projection都用同一个方法,但是返回值根据Projection动态调整呢?我们可以使用泛型来实现这个功能。

public interface UserRepository extends JpaRepository {
    List findByName(String name, Class type);
    T findByNameAndEmail(String name, String Email, Class type);
}

调用

void someMethod(UserRepository user) {
  User user = user.findByNameAndEmail("Matthews", "[email protected]", User.class);
  List userOnly = user.findByName("Matthews", UserOnly.class);
}

你可能感兴趣的:(Spring Data JPA: Projection)