Spring, JPA i order by count

0

W ramach nauki Springa piszę sobie prostą aplikacyjkę restową. Umożliwia ona dodawanie postów, komentarzy, upvotów. Używam do tego Spring Data JPA, do prostych operacji CRUD spisuje się to naprawdę dobrze.

Teraz moim zadaniem jest pobranie wszystkich postów posortowanych po ilości upvotów i komentarzy. JSON powinien zawierać takie pola jak: postid, userid, description, adddate, upvote count, comment count.
Pierwsza myśl to utworzenie widoku w bazie danych, oparcie encji na nim i voila. Zrobiłem, działa. Tylko znowu myślę sqlowo, bazo-centrycznie. No i nie jest persist.

Próbowałem przenieść to na obiekty, ale mi nie idzie. Zostałem z JPQL i trochę śmieciowym kodem (encję Users i Comments pomijam, są proste):

@Entity
public class Upvote {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long upvoteId;
    
    @JsonIgnore
    @ManyToOne
    private Users user;
    
    private UpvoteType type;   
    private Long toId;
    private Date addDate = new Date();

    public Upvote() {
    }

    public Upvote(Users user, UpvoteType type, Long toId) {
        this.user = user;
        this.type = type;
        this.toId = toId;
    }
//getters
}

public enum UpvoteType {
    
    POST, COMMENT;
    
}

Upvote można dać do postu i komentarza. Nie ma klucza obcego, tylko pole toId. W ten sposób chcę łatwo w przyszłości dodać upvote do filmów, zdjęć itp.
Nieszczęsny POST

@Entity
public class Post implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long postId;

    @ManyToOne
    private Users user;

    private UpvoteType upvoteType = UpvoteType.POST;

    @JsonIgnore
    @OneToMany
    @JoinColumns({
        @JoinColumn(updatable = false, insertable = false, name = "toId", referencedColumnName = "postId"),
        @JoinColumn(updatable = false, insertable = false, name = "type", referencedColumnName = "upvoteType")
    })
    private Set<Upvote> upvote;
        
    @OneToMany(
            mappedBy = "post",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List<Comments> comment = new ArrayList<>();    
    
    private String description;
    private Date addDate = new Date();

//constructors/getters
}

Chcę tylko wybrane pola, więc DTO:

public class GreatestPostsDto {

    private Long postId, userId;
    private String description;
    private Date addDate;
    private Long upvoteCount;
    private Long commentCount;

    public GreatestPostsDto(Long postId, Long userId, String description, Date addDate, Long upvoteCount, Long commentCount) {
        this.postId = postId;
        this.userId = userId;
        this.description = description;
        this.addDate = addDate;
        this.upvoteCount = upvoteCount;
        this.commentCount = commentCount;
    }
//getters

Repozytorium:

@Repository
public interface GreatestPostsRepository2 extends JpaRepository<Post, Long> {
    @Query(value = "select new pl.herasoft.test.springappka1.dto.GreatestPostsDto(p.postId, p.user.userId, p.description, p.addDate, count(upv.upvoteId) as upvoteCount, count(c.commentId) as commentCount) "
            + "from Post p "
            + "left join p.comment c "
            + "left join p.upvote upv "
            + "group by p.postId, p.user.userId, p.description, p.addDate "
            + "order by upvoteCount desc, commentCount desc")
    List<GreatestPostsDto> findGreatestPosts();
}

I wreszcie pytanie, czy da się to zrobić bez JPQL tylko na obiektach, tak żeby przy każdym requescie nie jechało po bazie?

1

Przecież JPQL jest niezależne od bazy danych, zadziała tak samo na Oracle, i na postgresie.

Ewentualnie jeśli używasz SpringData to możesz stworzyć metodę w interfejsie z odpowiednią nazwą, która Ci to pobierze
np. findByOrderByUpvoteCountDesc() - dokładnej składni nie pamiętam, ale coś w ten deseń, wtedy Spring Data tworzy implementacje.

Przykład:

List<LottoNumber> findByOrderByCountDesc();

Zwraca LottoNumber posortowane po Count Malejąco

0

Okej, w Spring Data mogę sortować wg takiej nazwy metody, jednak potrzebuję w encji pola upvoteCount, którego nie wiem jak ogarnąć tak żeby dane w nim były zawsze aktualne.
W encji Post dodałem:

private int upvoteCount = upvote.size();

Jednak wartość jest inicjalizowana tylko na starcie. Próbowałem różnych sposobów w setterami, ale wartość się nie aktualizuje.

Z kolei taki getter:

public int getUpvoteCount() {
        return upvote.size();
    }

wyświetli mi zawsze aktualną ilość, ale wtedy nie mogę po niej sortować. Jak to ogarnąć?

0

Tak z głowy, może przy zapisie upvote, inkrementuj też pole upvoteCount w encji, która jest właścicielem tego upvote'a.
Czyli dodaje do Post nowy upvote, a potem inkementuje pole z poście z liczbą upvoteów.

1 użytkowników online, w tym zalogowanych: 0, gości: 1