Как стать автором
Обновить

Комментарии 10

Честно не годится даже для учебного материала

Вы описываете модель , для работой с бд. Далее у вас

Data Transfer Object — нужны, чтобы перекидывать уменьшенные по полям сущностями внутри сервера: оперировать ими в запросах / получать из БД и тому подобное

Так что у вас с БД работает? Модели или дто?

@Data @NoArgsConstructor — аннотации Lombok. Реализуют некоторые методы. Какой метод реализует NoArgConstructor? И зачем данная аннотация вам?

Для чего вы используете вместе @Entity и Json аннотации?

ДИ лучше делать через конструктор

И многое другое....

Непосредственно с базой работают Entity и Repository. В моём понимании, DTO нужны для удобства представления данных. Не всегда хочется прокидывать сущность со всеми полями или в случае, когда для запроса понадобилась определённая структура. Я ошибаюсь в этом? UPD: осмотрел проект, в Repository я не использую DTO. Действительно, этот момент был глупостью, вы правы. Изменил этот кусок в статье, спасибо)

NoArgConstructor реализует конструктор без параметров. Требуется, соответственно, для создания экземпляра этого класса

Json аннотации нужны для сереализации, Entity для базы. На ваш взгляд, для каждой сущности стоит выделять отдельную DTO для API? Или каково ваше представление?

ДИ лучше делать через конструктор

Я и так делаю Dependency Injection через конструктор. Насколько я знаю, можно к полю в классе задать аннотацию Autowired, однако, в этом проекте я это вроде не использовал / что, впрочем, не возбраняется

Так в конструкторе можно и не указывать autowired, как у вас на фото UserServiceImpl

    final PasswordEncoder passwordEncoder;
    final UserRepository userRepository;
    final ChatUserService chatUserService;

    public UserServiceImpl(@Autowired PasswordEncoder passwordEncoder,
                           @Autowired UserRepository userRepository,
                           @Autowired ChatUserService chatUserService) {
        this.passwordEncoder = passwordEncoder;
        this.userRepository = userRepository;
        this.chatUserService = chatUserService;
    }

Я не совсем корректно вас понял, в данном конструкторе Autowired указан. Вы имеете ввиду, что его необязательно указывать вовсе? Или что-то иное?

Не обязательно, спринг сам разрулит

Использовать Page нежелательно, потому что это всегда 2 запроса к БД - 1 выбор данных, 2 подсчет числа доступных элементов. В вашем случае нет фильтров, но в если они будут, то можно легко повесить БД под нагрузкой таким образом.

Есть в спринге класс Slice - это тот же Page, но без вычисления количества страниц и элементов, желательно использовать именно его. Работать будет быстрее, а если нужно вычислять количество подходящих страниц и элементов, то фронту лучше делать 1 запрос в специальный эндпоинт для этого.

Правда в спринге как-то странно поддержка этого класса сделана, в последний раз когда с ним работал приходилось делать свою имплементацию репозитория, что бы спеку поддерживать:

@NoRepositoryBean
public class JpaSpecificationRepositoryWithSliceImpl<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID>
        implements JpaSpecificationRepositoryWithSlice<T, ID> {
    private final EntityManager em;

    public JpaSpecificationRepositoryWithSliceImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager em) {
        super(entityInformation, em);
        this.em = em;
    }

    @Transactional(readOnly = true)
    @Override
    public Slice<T> sliceAll(Specification<T> spec, Pageable pageable) {
        var list = this.getQuery(spec, pageable)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
        return new SliceImpl<>(list);
    }

    @Transactional(readOnly = true)
    @Override
    public Slice<T> sliceAll(Pageable pageable) {
        var cb = em.getCriteriaBuilder();
        var q = cb.createQuery(getDomainClass());
        var f = q.from(getDomainClass());
        q.select(f);
        q.orderBy(QueryUtils.toOrders(pageable.getSort(), f, cb));
        var list = em.createQuery(q)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
        return new SliceImpl<>(list);
    }
}

Разница между Page и Slice на 1 000 000 элементов даже без фильтров будет весьма ощутимой уже на глаз. В первом случае вся БД всегда читается, во втором только кусочек ограниченный размером страницы.

Можно взять любой произвольный класс и представить, что вы принимаете участие в Code Review. Возьмём класс UserServiceImpl. Вопросы:

*Какой интерфейс имплементирует данный класс?

*Почему поля класса package-private?

* Зачем нужна аннотация @Autowired в параметрах конструктора?

Спасибо за code review! Вы указали на ошибки, на которые я бы, вероятно, не обратил внимание в дальнейшем

UserServiceImpl не имплементирует никакой интерфейс/не наследует никакой класс. Под конец работы понял, что это не есть хорошо

Это мой плохой код, нужно добавлять private

В комментариях выше писали о том, что аннотация Autowired необязательна

К сожалению Вам ещё рано писать обучающие статьи...

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории