Spring Data JPA 自定义DTO类型的返回结果

562 阅读2分钟

1、自定义DTO返回结果

一般情况下,返回的字段和DB查询结果的字段是一一对应的。但有的时候需要返回一些指定的字段,或者返回一些复合型字段,而不需要全部返回。
Spring Data 允许对专用返回类型进行建模,有选择地返回同一个实体的不同试图对象

1.1 举例

User类

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Table(name = "user")
public class User {
    /**
     * 主键id
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private Integer age;

    @Column(name = "address")
    private String address;
}

如果我们只想返回User对象里面的name和address,应该怎么做呢?

1.1 第一种方式:直接定义一个DTO

新建一个DTO类来返回我们想要的字段,如下所示:

@Data
@AllArgsConstructor
public class UserDTO {
    private String name;
    private String address;
}

在UserRepository中构造查询方法,注意不要写findById这种语句,否则会报转化错误的异常

public interface UserRepo JpaRepository<User, Integer> {
    UserDTO findByIdAndName(int id,String name);
}

image.png

还需要注意的是,再看源码PreferredConstructorDiscoverer类时会发现,UserDTO里面只能有一个全参数构造方法。buildPreferredConstructor 会帮我们做构造参数的选择,如果UserDTO里面有多个构造方法,就会报转化错误的异常。

image.png

这种方式的优点就是返回的结果不需要一个实体对象。缺点就是构造方法不能更改,必须全参数。

1.2 第二种方式:返回结果是一个POJO的接口

定义一个UserNameAndAddress接口

public interface UserNameAndAddress {
    String getName();
    String getAddress();
}

UserNameAndAddress findByIdAndNameLike( int id,String name);

测试方法

@Test
public void findByUserInterface() {
    UserNameAndAddress e = userRepo.findByIdAndNameLike(1,"老王");
    System.out.println(e.getAddress());
    System.out.println(e.getName());

}

这种方式非常灵活,不需要添加构造方法。在运行过程中,UserNameAndAddress接口成了一个代理对象,里面通过Map的格式包含了我们要返回的字段的值(如name,address),用的时候直接调用接口里面的方法即可。

1.3 不同DTO类型生成的JPQL语句也不同

返回DTO接口形式的Query生成的JPQL语句如下

image.png

返回DTO类的时候QueryStructure生成的JPQL语句如下,两者最大的区别是DTO类需要用到构造方法新建一个对象出来

image.png