如何按具有不同 JPA 规范的获取属性进行排序
How to sort by fetched properties with distinct JPA Specifications
我使用 Spring Boot 1.5.3.RELEASE
,对我来说,不清楚如何使用 distinct
和 Specifications
按嵌套对象的属性排序,因为:
Caused by: org.postgresql.util.PSQLException: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
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;
}
我们有 Vehicle
class 嵌套对象 VehicleType
和 VehicleBrand
。
@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;
}
Class 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;
}
服务
现在我想使用 JPA Specifications
创建一个查询并按 "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"));
}
Spring 数据 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
而且它完全不起作用,因为:
Order by expression "VEHICLEBRA1_.NAME" must be in the result list in this case; SQL statement
要解决这个问题,我们必须在 Specification
:
中获取 vehicleBrand
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"));
}
Spring 数据 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
现在可以使用了,因为我们在 selection 部分看到了 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"));
}
它生成以下查询:
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
它不起作用是因为:
Order by expression "VEHICLEMOD4_.NAME" must be in the result list in this case; SQL statement
看看我们如何 select 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;
}
);
我应该怎么做才能使 JPA 生成正确的查询,以便我可以按嵌套对象进行排序?
将 Spring Boot 的版本从 1.5.3.RELEASE
升级到 2+
是否有意义?
谢谢。
这里有一个小秘密:您根本不需要使用 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));
Sort
参数可能是在您的方案中添加额外连接的原因。
我使用 Spring Boot 1.5.3.RELEASE
,对我来说,不清楚如何使用 distinct
和 Specifications
按嵌套对象的属性排序,因为:
Caused by: org.postgresql.util.PSQLException: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
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;
}
我们有 Vehicle
class 嵌套对象 VehicleType
和 VehicleBrand
。
@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;
}
Class 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;
}
服务
现在我想使用 JPA Specifications
创建一个查询并按 "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"));
}
Spring 数据 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
而且它完全不起作用,因为:
Order by expression "VEHICLEBRA1_.NAME" must be in the result list in this case; SQL statement
要解决这个问题,我们必须在 Specification
:
vehicleBrand
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"));
}
Spring 数据 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
现在可以使用了,因为我们在 selection 部分看到了 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"));
}
它生成以下查询:
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
它不起作用是因为:
Order by expression "VEHICLEMOD4_.NAME" must be in the result list in this case; SQL statement
看看我们如何 select 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;
}
);
我应该怎么做才能使 JPA 生成正确的查询,以便我可以按嵌套对象进行排序?
将 Spring Boot 的版本从 1.5.3.RELEASE
升级到 2+
是否有意义?
谢谢。
这里有一个小秘密:您根本不需要使用 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));
Sort
参数可能是在您的方案中添加额外连接的原因。