如何按具有不同 JPA 规范的获取属性进行排序

Rom*_*lov 7 sorting jpa distinct criteria-api spring-data-jpa

我使用 Spring Boot 1.5.3.RELEASE,对我来说,不清楚如何按嵌套对象的属性进行排序distinctSpecifications原因如下:

引起:org.postgresql.util.PSQLException:错误:对于 SELECT DISTINCT,ORDER BY 表达式必须出现在选择列表中

Spring Data JPA 生成错误的查询。

让我们看一个小例子:

模型

@Data
@Entity
@Table(name = "vehicle")
public class Vehicle implements Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "vehicle_type_id")
    private VehicleType vehicleType;

    @ManyToOne
    @JoinColumn(name = "vehicle_brand_id")
    private VehicleBrand vehicleBrand;
}
Run Code Online (Sandbox Code Playgroud)

我们有Vehicle带有嵌套对象的类VehicleTypeVehicleBrand.

@Data
@Entity
@Table(name = "vehicle_brand")
public class VehicleBrand implements Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

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

    @ManyToOne
    @JoinColumn(name = "vehicle_model_id")
    private VehicleModel model;

}
Run Code Online (Sandbox Code Playgroud)

VehicleBrand还包含VehicleModel.

@Data
@Entity
@Table(name = "vehicle_model")
public class VehicleModel implements Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;
}
Run Code Online (Sandbox Code Playgroud)

服务

现在我想用 JPASpecifications和一些排序来创建一个查询"vehicleBrand.name"

public List<Vehicle> findAll() {
    Specification<Vehicle> spec = Specifications.where(
            (root, criteriaQuery, criteriaBuilder) -> {
                criteriaQuery.distinct(true);
                return null;
            }
    );
    return vehicleRepository.findAll(spec, new Sort("vehicleBrand.name"));
}
Run Code Online (Sandbox Code Playgroud)

Spring Data JPA 生成以下查询:

select
    distinct vehicle0_.id as id1_0_,
    vehicle0_.gas_type as gas_type2_0_,
    vehicle0_.vehicle_brand_id as vehicle_4_0_,
    vehicle0_.vehicle_type_id as vehicle_5_0_,
    vehicle0_.year_of_issue as year_of_3_0_ 
from
    vehicle vehicle0_ 
left outer join
    vehicle_brand vehiclebra1_ 
        on vehicle0_.vehicle_brand_id=vehiclebra1_.id 
order by
    vehiclebra1_.name asc
Run Code Online (Sandbox Code Playgroud)

它完全不起作用,因为:

在这种情况下,按表达式“VEHICLEBRA1_.NAME”排序必须在结果列表中;SQL语句

要解决这个问题,我们必须vehicleBrand在我们的Specification

public List<Vehicle> findAll() {
    Specification<Vehicle> spec = Specifications.where(
            (root, criteriaQuery, criteriaBuilder) -> {
                criteriaQuery.distinct(true);
                root.fetch("vehicleBrand", JoinType.LEFT); //note that JoinType.INNER doesn't work in that case
                return null;
            }
    );
    return vehicleRepository.findAll(spec, new Sort("vehicleBrand.name"));
}
Run Code Online (Sandbox Code Playgroud)

Spring Data JPA 生成以下查询:

select
        distinct vehicle0_.id as id1_0_0_,
        vehiclebra1_.id as id1_1_1_,
        vehicle0_.gas_type as gas_type2_0_0_,
        vehicle0_.vehicle_brand_id as vehicle_4_0_0_,
        vehicle0_.vehicle_type_id as vehicle_5_0_0_,
        vehicle0_.year_of_issue as year_of_3_0_0_,
        vehiclebra1_.vehicle_model_id as vehicle_3_1_1_,
        vehiclebra1_.name as name2_1_1_ 
    from
        vehicle vehicle0_ 
    left outer join
        vehicle_brand vehiclebra1_ 
            on vehicle0_.vehicle_brand_id=vehiclebra1_.id 
    order by
        vehiclebra1_.name asc
Run Code Online (Sandbox Code Playgroud)

现在它起作用了,因为我们vehiclebra1_.name在选择部分看到。

但是如果我需要排序"vehicleBrand.model.name"怎么办?
我做了一个额外的fetch,但它不起作用:

public List<Vehicle> findAll() {
    Specification<Vehicle> spec = Specifications.where(
            (root, criteriaQuery, criteriaBuilder) -> {
                criteriaQuery.distinct(true);
                root.fetch("vehicleBrand", JoinType.LEFT).fetch("model", JoinType.LEFT);
                return null;
            }
    );
    return vehicleRepository.findAll(spec, new Sort("vehicleBrand.model.name"));
}
Run Code Online (Sandbox Code Playgroud)

它生成以下查询:

select
        distinct vehicle0_.id as id1_0_0_,
        vehiclebra1_.id as id1_1_1_,
        vehiclemod2_.id as id1_2_2_,
        vehicle0_.gas_type as gas_type2_0_0_,
        vehicle0_.vehicle_brand_id as vehicle_4_0_0_,
        vehicle0_.vehicle_type_id as vehicle_5_0_0_,
        vehicle0_.year_of_issue as year_of_3_0_0_,
        vehiclebra1_.vehicle_model_id as vehicle_3_1_1_,
        vehiclebra1_.name as name2_1_1_,
        vehiclemod2_.name as name2_2_2_ 
    from
        vehicle vehicle0_ 
    left outer join
        vehicle_brand vehiclebra1_ 
            on vehicle0_.vehicle_brand_id=vehiclebra1_.id 
    left outer join
        vehicle_model vehiclemod2_ 
            on vehiclebra1_.vehicle_model_id=vehiclemod2_.id cross 
    join
        vehicle_model vehiclemod4_ 
    where
        vehiclebra1_.vehicle_model_id=vehiclemod4_.id 
    order by
        vehiclemod4_.name asc
Run Code Online (Sandbox Code Playgroud)

它不起作用,因为:

在这种情况下,按表达式“VEHICLEMOD4_.NAME”排序必须在结果列表中;SQL语句

看看我们如何选择vehiclemod2_.name但按 排序vehiclemod4_.name

我试图Specification直接进行排序,但它也不起作用:

Specification<Vehicle> spec = Specifications.where(
        (root, criteriaQuery, criteriaBuilder) -> {
            criteriaQuery.distinct(true);
            root.fetch("vehicleBrand", JoinType.LEFT).fetch("model", JoinType.LEFT);
            criteriaQuery.orderBy(criteriaBuilder.asc(root.join("vehicleBrand", JoinType.LEFT).join("model", JoinType.LEFT).get("name")));
            return null;
        }
);
Run Code Online (Sandbox Code Playgroud)

我应该怎么做才能让 JPA 生成正确的查询,以便我可以按嵌套对象进行排序?是否有意义,从升级春季启动的版本1.5.3.RELEASE2+
谢谢。

cri*_*zis 3

Sort这里有一个小秘密:您根本不需要使用该参数。

只需使用CriteriaQuery.orderBy

Specification<Vehicle> spec = Specifications.where(
            (root, criteriaQuery, criteriaBuilder) -> {
                criteriaQuery.distinct(true);
                var model = root.fetch("vehicleBrand", JoinType.LEFT).fetch("model", JoinType.LEFT);
                criteriaQuery.orderBy(criteriaBuilder.asc(model.get("name"));
                return null;
            }
    );
    return vehicleRepository.findAll(spec));
Run Code Online (Sandbox Code Playgroud)

Sort参数可能是在您的场景中添加额外连接的参数。