Reputation: 456
I have an entity Mission
which has a collection of entities Trip
, and I would like to be able to sort the mission in function of the content in the collection of Trip
.
The Trip
entity:
@Entity
public Class Trip {
@Id private Long id;
@Column private LocalDateTime time;
}
The Mission
entity:
@Entity
public Class Mission {
@Id private Long id;
@Column private String city;
@OneToMany(mappedBy = "mission")
private Collection<Trip> trips;
}
The Mission
Repository:
@Repository
public interface MissionRepository extends PagingAndSortingRepository<Mission, Long>, QuerydslPredicateExecutor<Mission>, QuerydslBinderCustomizer<QMission> {
@Override
default void customize(final QuerydslBindings bindings, final QMission mission) {
bindings.bind(String.class).first((final StringPath path, final String value) -> path.containsIgnoreCase(value));
bindings.including(mission.city);
bindings.excludeUnlistedProperties(true);
}
Page<T> findAll(Predicate predicate, Pageable pageable);
}
The Mission
Service:
@Service
@Transactional
public class MissionService {
private final MissionRepository missionRepository;
@Autowired
public MissionService(final MissionRepository missionRepository) {
this.missionRepository = missionRepository;
}
public Page<Mission> findAll(final Predicate predicate, final Pageable pageable) {
return this.missionRepository.findAll(predicate, pageable);
}
}
The Mission
Controller:
@RestController
@RequestMapping("/api/missions")
public class MissionController {
private final MissionService missionService;
@Autowired
public MissionController(final MissionService missionService) {
this.missionService = missionService;
}
@GetMapping
@ResponseBody
public Page<Mission> getAllMissions(@QuerydslPredicate(root = Mission.class) final Predicate predicate, @PageableDefault final Pageable pageable) {
return this.missionService.findAll(predicate, pageable);
}
}
When calling /api/missions?sort=city,DESC
, it work perfectly.
But when calling /api/missions?sort=trips.time,DESC
I am getting duplicate entries which match the number of Trips linked with a Mission.
All these mission are correctly sorted, but the duplicate part is just a no go…
The objective is to be able to sort the Mission
(ASC
or DESC
) in function of the departure date from the Trip
collection (which would be the first Trip
sorted in ASC
).
So, how to sort this list of Mission
on the content from the collection of Trip
without getting the duplication part?
I would also like to avoid changing the REST method signature or the database structure, if possible.
I was thinking of adding a DISTINCT
on the list, but It wouldn't work as the sorting need to be on the first Trip's time field. Sorting in ASC
mode would work. But in DESC
, it would then be sorted in function of the arrival date, and not the departure…
A crazy idea: would it be possible to automatically fill a transient property in the mission with the content of a query, and sort on it using the Pageable
class?
Upvotes: 1
Views: 1952
Reputation: 456
I was able to fix my problem by using the @Formula
annotation from Hibernate.
Mission
entity@Formula("(select min(t.time) from Trip t where t.mission_id = id)")
private LocalDateTime startDate;
We just need to call the property.
/api/missions?sort=startDate,DESC
.
Currently I don't have any performance issue. Yet, just in case, I am also considering to use a view which will then be linked to my entity.
NB: This one wasn't test, but is based on my understanding from the following blog.
Trip
and Mission
CREATE OR REPLACE VIEW mission_derived AS
select
m.`id` as id,
min(t.`time`) as startDate
from `trip` t
inner join `mission` m on t.`mission_id` = m.`id`
group by m.`id`;
@Entity
@Immutable
public class MissionDerived {
@Id
private long id;
@Column
private Date startDate;
}
Mission
entity@OneToOne
@PrimaryKeyJoinColumn(name="ID")
private MissionDerived derived;
We would then need to call the property from the mission, and then the property from the derived entity.
/api/missions?sort=derived.startDate,DESC
Upvotes: 1