Имейте свойство в объекте, которое содержит общее количество дочерних элементов объекта отношения - PullRequest
0 голосов
/ 05 января 2019

У меня есть сущность Вопрос и сущность Ответ. Я хочу, чтобы у вопроса было свойство, содержащее количество ответов на него.

Я знаю, что могу определить отношение в Вопросе, чтобы все ответы загружались вместе с вопросом, но это не очень эффективно. Я загружаю все ответы на вопрос в отдельном запросе.

Я знаю, что я также могу подсчитать, сколько ответов (используя метод репозитория) имеет вопрос, но я бы хотел, чтобы эта логика была связана с сущностью Вопрос, чтобы в идеале выполнялись как подсчет ответов, так и загрузка объекта вопроса. в том же SQL-запросе JPA, иначе запрос должен выполнить 20 запросов SELECT COUNT только для получения количества ответов на вопрос, поскольку каждый раз загружается 20 вопросов.

Возможно ли это достичь с помощью Spring JPA?

1 Ответ

0 голосов
/ 10 января 2019

Я нашел решение своей проблемы после долгих поисков и испытаний и с помощью @marnish и @ Augusto.

Мне нужно начать с вопроса, что я неправильно задал свой вопрос, потому что я хотел знать, как я могу внедрить эти данные в свою сущность, в то время как такой образ мыслей заставлял меня испытывать большие трудности с поиском решения, потому что это ограничено тем, чего я смог достичь. Лучше было отделить мой объект базы данных от моих объектов JSON, потому что один предназначен для сохранения данных, другой для представления клиенту и первый способ практически не связан со столбцами базы данных. Переходные свойства не были здесь решением и для меня.

Понимая (и принимая) это, следующая проблема состояла в том, чтобы заставить новый объект презентации хорошо играть с упаковщиками страниц, потому что мне нужно было разбивать страницы на свои Вопросы. Вот что я написал в качестве решения:

Вопрос как сущность, связанная с таблицей базы данных и столбцами

@Entity
@Table(name = "vragen")
// BaseEntity just contains the ID property
public class Question extends BaseEntity {
    @Column(name = "vraag")
    private String question;

    @Column(name = "bericht")
    private String message;

    @ManyToOne
    @JoinColumn(name = "gebruiker")
    private User user;

    @Column(name = "beantwoord", insertable = false)
    private boolean answered;

    @Column(name = "gepost_op", insertable = false)
    private Timestamp postedOn;

    @Column(name = "actief", insertable = false)
    @JsonIgnore
    private boolean active;

    @Column(name = "bekeken", insertable = false, updatable = false)
    private int viewed;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "vragen_tags",
            joinColumns = @JoinColumn(name = "vraag_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id"))
    private Set<Tag> tags;

    @OneToMany(mappedBy = "question")
    // Thanks to @manish we can do it more efficiently instead of loading all the Answer objects
    @LazyCollection(LazyCollectionOption.EXTRA)
    @JsonManagedReference
    private Set<Answer> answers;


    public Question() {}

    // getters and setters
}

QuestionDTO как объект представления JSON

public class QuestionDTO {
    private Integer id;
    private String question;
    private String message;
    private User user;
    private boolean answered;
    private Timestamp postedOn;
    private int viewed;
    private Set<Tag> tags;
    private int totalAnswers;


    public QuestionDTO() {}

    // getters and setters
}

Метод в QuestionServiceImpl:

public Page<QuestionDTO> getAllQuestions(int page, int size, String filter) {
    List<QuestionDTO> questions = new ArrayList<>();
    Page<Question> questionPage;

    switch (filter) {
        case "popular":
            questionPage = questionRepository.findAllByActiveTrueOrderByViewedDescPostedOnDesc(PageRequest.of(page, size));
            break;

        case "answered":
            questionPage = questionRepository.findAllByActiveTrueAndAnsweredTrueOrderByPostedOnDesc(PageRequest.of(page, size));
            break;

        default:
            questionPage = questionRepository.findAllByActiveTrueOrderByPostedOnDesc(PageRequest.of(page, size));
            break;
    }

    // Here the entity data is being converted to the presentation DTO. I'm sure this can be done cleaner with for example Mapstruct as objectmapper.
    for (Question q : questionPage) {
        QuestionDTO questionDTO = new QuestionDTO();

        questionDTO.setId(q.getId());
        questionDTO.setQuestion(q.getQuestion());
        questionDTO.setMessage(q.getMessage());
        questionDTO.setUser(q.getUser());
        questionDTO.setAnswered(q.isAnswered());
        questionDTO.setPostedOn(q.getPostedOn());
        questionDTO.setViewed(q.getViewed());
        questionDTO.setTags(q.getTags());
        questionDTO.setTotalAnswers(q.getAnswers().size());

        questions.add(questionDTO);
    }
    // We want to keep all the pagination data that the method returned, so we need to wrap the questionDTO in a Page wrapper and pass all the data from Page<Question>
    return new PageImpl<>(questions, questionPage.getPageable(), questionPage.getTotalElements());
}

Ответный субъект:

@Entity
@Table(name = "antwoorden")
public class Answer extends BaseEntity {
    @Column(name = "bericht")
    private String message;

    @ManyToOne
    @JoinColumn(name = "gebruiker")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "vraag")
    @JsonBackReference
    private Question question;

    @Column(name = "gepost_op", insertable = false)
    private Timestamp postedOn;

    @Column(name = "actief", insertable = false)
    private boolean active;


    public Answer() {}

    // getters and setters
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...