Skip to content

Abdulkhanov mod4 typical project#2

Open
AbdulkhanovMT wants to merge 13 commits intoSchreiber888:mainfrom
AbdulkhanovMT:abdulkhanov_mod4_typical_project
Open

Abdulkhanov mod4 typical project#2
AbdulkhanovMT wants to merge 13 commits intoSchreiber888:mainfrom
AbdulkhanovMT:abdulkhanov_mod4_typical_project

Conversation

@AbdulkhanovMT
Copy link
Copy Markdown

Typical version of the final project from module 4.

Copy link
Copy Markdown

@demologin demologin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Общий вывод по проекту

Проект представляет собой хорошую рабочую заготовку для системы миграции данных. Видно понимание объектно-реляционного отображения (ORM) и работы с NoSQL хранилищами. Основные замечания касаются архитектурного слоя: жесткая связанность компонентов (Tight Coupling) и отсутствие автоматизированного управления зависимостями. Также стоит уделить больше внимания безопасности типов при работе с результатами Hibernate-запросов.


@Getter
@Setter
@RequiredArgsConstructor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аннотация @requiredargsconstructor не имеет смысла, так как в классе отсутствуют final поля. Рекомендуется использовать @NoArgsConstructor или добавить модификатор final к полям. [WARNING]

@RequiredArgsConstructor
public class Language {
private String language;
private Boolean isOfficial;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование префикса 'is' для поля типа Boolean может привести к некорректной генерации геттеров библиотекой Lombok (например, isOfficial() вместо getIsOfficial()), что иногда нарушает стандарты JavaBeans. [INFO]

@Getter
@Setter
@RequiredArgsConstructor
public class Language {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В классе отсутствуют методы equals() и hashCode(). Для объектов, которые могут использоваться в коллекциях (Set), их реализация обязательна для корректного сравнения данных. [WARNING]


@Getter
@Setter
@RequiredArgsConstructor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аннотация @requiredargsconstructor здесь избыточна, так как нет final полей. Для чистоты кода следует использовать соответствующую аннотацию или определить конструктор явно. [WARNING]


private Integer countryPopulation;

private Set<Language> languages;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование Set предполагает наличие реализации equals и hashCode в классе Language. Без них Set будет работать как List, допуская дубликаты по ссылке. [ERROR]

public class CityDAO {
private final SessionFactory sessionFactory;

public CityDAO(SessionFactory sessionFactory) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Передача SessionFactory в каждый DAO вручную усложняет архитектуру. В современных Java-приложениях этот процесс автоматизируется через DI-контейнеры. [WARNING]

@@ -0,0 +1,16 @@
package com.javarush.redis;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Классы в пакете 'redis' фактически являются DTO (Data Transfer Objects) для кэша. Рекомендуется добавить суффикс Dto к именам классов для ясности намерений. [INFO]

}


public static void main(String[] args) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод main содержит логику создания приложения. Рекомендуется разделять запуск приложения и выполнение бизнес-задач (например, через метод run()). [WARNING]


public class Main {

private final SessionFactory sessionFactory;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поля sessionFactory и redisClient должны закрываться (close()) при завершении работы приложения. В коде не предусмотрен механизм закрытия ресурсов (Graceful Shutdown). [ERROR]


private final ObjectMapper mapper;

private final CityDAO cityDAO;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для улучшения читаемости в Java 21 при инициализации локальных переменных внутри методов (например, в main или методах подготовки) следует чаще использовать ключевое слово var. [INFO]

@demologin
Copy link
Copy Markdown

Общий вывод по проекту

Проект представляет собой хорошую рабочую заготовку для системы миграции данных. Видно понимание объектно-реляционного отображения (ORM) и работы с NoSQL хранилищами. Основные замечания касаются архитектурного слоя: жесткая связанность компонентов (Tight Coupling) и отсутствие автоматизированного управления зависимостями. Также стоит уделить больше внимания безопасности типов при работе с результатами Hibernate-запросов.


@Getter
@Setter
@RequiredArgsConstructor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аннотация @requiredargsconstructor не имеет смысла, так как в классе отсутствуют final поля. Рекомендуется использовать @NoArgsConstructor или добавить модификатор final к полям.

@RequiredArgsConstructor
public class Language {
private String language;
private Boolean isOfficial;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование префикса 'is' для поля типа Boolean может привести к некорректной генерации геттеров библиотекой Lombok (например, isOfficial() вместо getIsOfficial()), что иногда нарушает стандарты JavaBeans.

@Getter
@Setter
@RequiredArgsConstructor
public class Language {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В классе отсутствуют методы equals() и hashCode(). Для объектов, которые могут использоваться в коллекциях (Set), их реализация обязательна для корректного сравнения данных.


@Getter
@Setter
@RequiredArgsConstructor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аннотация @requiredargsconstructor здесь избыточна, так как нет final полей. Для чистоты кода следует использовать соответствующую аннотацию или определить конструктор явно.


private Integer countryPopulation;

private Set<Language> languages;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование Set предполагает наличие реализации equals и hashCode в классе Language. Без них Set будет работать как List, допуская дубликаты по ссылке.

}

public List<City> getItems(int offset, int limit) {
Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c", City.class);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HQL-запрос жестко прописан в коде метода. Рекомендуется выносить такие строки в константы или использовать NamedQueries для улучшения читаемости и поддержки.

}

public List<City> getItems(int offset, int limit) {
Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c", City.class);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вызов getCurrentSession() предполагает наличие активной транзакции. В коде DAO не видно управления границами транзакций, что может привести к HibernateException.


public int getTotalCount() {
Query<Long> query = sessionFactory.getCurrentSession().createQuery("select count(c) from City c", Long.class);
return Math.toIntExact(query.uniqueResult());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод uniqueResult() может вернуть null, если записи отсутствуют. Передача null в Math.toIntExact приведет к NullPointerException. Требуется предварительная проверка.

public City getById(Integer id) {
Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c join fetch c.country where c.id = :ID", City.class);
query.setParameter("ID", id);
return query.getSingleResult();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод getSingleResult() выбрасывает NoResultException, если сущность не найдена. В современных приложениях на Java 21 предпочтительнее использовать uniqueResultOptional() для обработки отсутствия данных.

}

public City getById(Integer id) {
Query<City> query = sessionFactory.getCurrentSession().createQuery("select c from City c join fetch c.country where c.id = :ID", City.class);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование 'join fetch c.country' является отличной практикой для предотвращения проблемы N+1. Это свидетельствует о глубоком понимании работы Hibernate.

public List<Country> getAll() {
Query<Country> query = sessionFactory
.getCurrentSession()
.createQuery("select c from Country c join fetch c.languages", Country.class);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Текст запроса следует вынести в статическую константу класса. Это упростит тестирование и сделает код более чистым согласно принципам Clean Code.

this.sessionFactory = sessionFactory;
}

public List<Country> getAll() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод getAll() загружает все записи из таблицы стран. При росте объема данных это может вызвать переполнение памяти (OutOfMemoryError). Рекомендуется реализовать пагинацию.

private final SessionFactory sessionFactory;
private final RedisClient redisClient;

private final ObjectMapper mapper;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле mapper следует пометить модификатором final, так как оно инициализируется один раз в конструкторе и не изменяется в дальнейшем.

private final CityDAO cityDAO;
private final CountryDAO countryDAO;

public Main() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Конструктор содержит сложную логику инициализации ресурсов (БД, Redis). Согласно принципам SOLID, конструкторы должны заниматься только присваиванием зависимостей.


public Main() {
sessionFactory = prepareRelationalDb();
cityDAO = new CityDAO(sessionFactory);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ручное создание экземпляров DAO (new CityDAO) создает жесткую связанность. Рекомендуется внедрять зависимости через конструктор, что упростит модульное тестирование.

countryDAO = new CountryDAO(sessionFactory);

redisClient = prepareRedisClient();
mapper = new ObjectMapper();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Создание нового экземпляра ObjectMapper в каждом экземпляре Main неэффективно. Этот объект потокобезопасен и должен использоваться как синглтон.

import io.lettuce.core.api.sync.RedisStringCommands;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Класс Configuration для настройки Hibernate считается устаревшим. В новых проектах рекомендуется использовать ServiceRegistry и MetadataSources.

import java.util.Set;
import java.util.stream.Collectors;

import static java.util.Objects.nonNull;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Статический импорт Objects.nonNull не используется в представленном фрагменте кода. Неиспользуемые импорты следует удалять для поддержания чистоты проекта.

import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

import java.util.ArrayList;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Импорт java.util.ArrayList не используется. Это захламляет секцию импортов и затрудняет чтение кода.

public class Main {

private final SessionFactory sessionFactory;
private final RedisClient redisClient;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Инициализация тяжеловесного RedisClient в конструкторе затрудняет создание объекта Main. Такую логику лучше вынести в отдельный метод инициализации или жизненного цикла.

public class CityDAO {
private final SessionFactory sessionFactory;

public CityDAO(SessionFactory sessionFactory) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Передача SessionFactory в каждый DAO вручную усложняет архитектуру. В современных Java-приложениях этот процесс автоматизируется через DI-контейнеры.

@@ -0,0 +1,16 @@
package com.javarush.redis;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Классы в пакете 'redis' фактически являются DTO (Data Transfer Objects) для кэша. Рекомендуется добавить суффикс Dto к именам классов для ясности намерений.

}


public static void main(String[] args) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод main содержит логику создания приложения. Рекомендуется разделять запуск приложения и выполнение бизнес-задач (например, через метод run()).


public class Main {

private final SessionFactory sessionFactory;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поля sessionFactory и redisClient должны закрываться (close()) при завершении работы приложения. В коде не предусмотрен механизм закрытия ресурсов (Graceful Shutdown).


private final ObjectMapper mapper;

private final CityDAO cityDAO;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для улучшения читаемости в Java 21 при инициализации локальных переменных внутри методов (например, в main или методах подготовки) следует чаще использовать ключевое слово var.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants