8

I use Spring Boot 1.5.3.RELEASE and for me it's unclear how to sort by properties of nested objects with distinct and Specifications because of:

Caused by: org.postgresql.util.PSQLException: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list

Spring Data JPA generates wrong query.

Let's see a little example:

Model

@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; } 

We have Vehicle class with nested objects VehicleType and 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 also contains 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; } 

Service

Now I want to create a query with JPA Specifications and some sorting by "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 Data JPA generates following query:

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 

And it fairly doesn't work because of:

Order by expression "VEHICLEBRA1_.NAME" must be in the result list in this case; SQL statement

To fix the issue we have to fetch vehicleBrand in our 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")); } 

Spring Data JPA generates following query:

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 

And now it works because we see vehiclebra1_.name in the selection part.

Question

But what to do If I need to sort by "vehicleBrand.model.name"?
I make an additional fetch, but it doesn't work:

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")); } 

It generates following query:

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 

And it doesn't work because of:

Order by expression "VEHICLEMOD4_.NAME" must be in the result list in this case; SQL statement

Take a look on how we select vehiclemod2_.name but make order by vehiclemod4_.name.

I've tried to make sorting in Specification directly but it also doesn't work:

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; } ); 

What should I do to make JPA generate right query so I could make a sorting by nested objects? Does it make sense to upgrade version of Spring Boot from 1.5.3.RELEASE to 2+?
Thanks.

3
  • I'm wondering why you need .distinct(true) in your query, since you're only joining along to-one associations. What happens if you remove it and use regular joins instead? Commented Apr 13, 2021 at 16:59
  • @crizzis,I have much more complicated model and queries via Specification, to simplify it I've made a little example. So in real case it's required Commented Apr 14, 2021 at 8:31
  • i get this issue, but need paging, so i lot of modification need to be done to be able to use orderBy this way Commented Dec 15, 2023 at 23:43

1 Answer 1

4
+50

Here's a little secret: you don't need to use the Sort parameter at all.

Just use 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)); 

The Sort parameter is likely what's adding the extra join in your scenario.

Sign up to request clarification or add additional context in comments.

8 Comments

Mismatch types. root.fetch(...) returns Fetch, not Join
Sorry, my bad. Changed to var, in any case, you should be able to call .get() on the result
If only I could, but with Fetch result I can only use fetch(..) method. There is no possibility to use get(...)
yes It's castable to Join at runtime and It seems it works
Is there any possibility to make it with Sort somehow? Why does it make extra join?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.