mercredi 27 février 2019

Spring jpa: order by field of a nested collection conditional member

This question is not specific. I'm just a junior developer asking for advice with overwhelming task. If it's not how things are done here at Stackoverflow, please tell.

Let's imagine we need an app to monitor government messages. We have the following model:

@Entity
class Message {
    @Id
    @Column(name = "MESSAGE_ID")
    private Integer id;

    @OneToMany
    @JoinColumn(name = "MESSAGE_ID", referencedColumnName = "MESSAGE_ID")
    private List<Dispatch> dispatches;

    private String text;

    //getters & setters
}

@Entity
class Dispatch {
    @Id
    @Column(name = "DISPATCH_ID")
    private Integer id;

    private String destination; //few dozen of different destinations

    @OneToMany
    @JoinColumn(name = "DISPATCH_ID", referencedColumnName = "DISPATCH_ID")
    private List<Status> statuses;

    //getters & setters
}

@Entity
class Status {
    @Id
    @Column(name = "STATUS_ID")
    private Integer id;

    private String code; //one of those: "SENT", "DELIVERED", "READ"

    private Date registered;

    //getters & setters
}

Controller and repository look like this:

@RestController
class MessageController {
    @Autowired
    private MessageRepository repository;

    @GetMapping("/messages")
    public Page<Message> getMessagesSorted(Pageable pageable) {
        return repository.findAll(pageable);
    }
}

interface MessageRepository extends JpaRepository<Message, Integer> {
}

And if we call our API like "../messages?size=50&page=3&sort=id, asc" we'll get exactly what we want: 4-th page with 50 elements sorted by id.

But next thing we want is to sort messages by the date they were read, delivered or sent to specific destination. For example "../messages?sort=sentToMoscow, desc"

First thought is to make custom method in repository and annotate it with @Query.

But there's gonna be a lot of different sorting scenarios. And more importantly, we are using JPA Criteria API (or Spring Specifications) to dynamically build other filtering restrictions.

One thing I came up with is @Formula:

class Message {
    /*
    ...
    */

    @Formula("(select * from " +
                 "(select s.saved from message m " +
                    "join dispatch d on d.message_id = m.message_id " +
                    "join status s on s.dispatch_id = d.dispatch_id " +
                  "where m.message_id = message_id " +
                    "and d.destination = 'MOSCOW' and s.code = 'SENT') " +
              "where rownum = 1)")
    private Date sentToMoscow;

    //getters & setters
}

But it looks ugly even when it's alone. If I add more, it will be a disaster.

So the question is how can I implement sorting and keep my job?

Aucun commentaire:

Enregistrer un commentaire