Skip to content

Latest commit

 

History

History
244 lines (189 loc) · 29.9 KB

File metadata and controls

244 lines (189 loc) · 29.9 KB

Онлайн-проект Topjava

correction Правки в проекте

Apply 5_0_1_fix_tests.patch

  • Переименовал переменные в MealRepository.getBetweenHalfOpen()/реализациях на ...DateTime
  • Вернул в spring-db.xml postgres как база по умолчанию
  • Переделал jdbc.initLocation: не "тупит", если путь к скрипту полностью прописывать в пропертях.

Apply 5_0_2_fix_tests.patch

  • Правки расположения полей и названий методов в тестах
  • Можно ставить любую версию JDK: 11, 12 или 13. В проекте оставил 11, т.к. она LTS (long term support) и на 12 еще мало проектов. Для JDK 12/13 нужно поправить 2 места:
    • pom.xml - <java.version>xx</java.version>
    • .travis.yml - jdk: openjdkxx
  • Для запуска Tomcat под JDK11/12/13 не из IDEA проверь переменную окружения JAVA_HOME (версия java в path проверяется просто: java -version) и версию Tomcat 9.x.

Apply 5_1_jdk_11.patch

hw Разбор домашнего задания HW4

EntityManager - это по сути прокси-обертка над Hibernate Session, которая создается каждый раз при открытии транзакции.

  • Дополнительно (ни разу не сталкивался): еще есть редкий случай ручного управления @PersistenceContext(type = PersistenceContextType.EXTENDED), когда он используется в нескольких транзакциях (long-running session or session-per-conversation).

Apply 5_2_HW4.patch

  • При сравнении еды тесты падают, т.к. Hibernate делает ленивую обертку к user, и если происходит обращение к любому его полю (кроме id) вне транзакции, бросается LazyInitializationException. По логике приложения поле user в еде не нужно, и мы не будем его отдавать наружу: в тестах исключаем user из сравнения.
  • Поменял реализацию JpaMealRepository.get() (вместо @NamedQuery), реализация стали проще
  • Сделал JpaMealRepository.save проще и понятнее (em.getReference не делает запрос в базу и чужая еда- не основной юзкейс)
  • Вместо BETWEEN (и CAST) в JPA реализации сделал Half Open сравнение. См. также SQL “between” not inclusive

Apply 5_3_fix_hibernate_issue.patch


Переопределять equals()/hashCode() необходимо, если

  • использовать entity в Set (рекомендовано для Many-ассоциаций) либо как ключи в HashMap
  • использовать reattachment of detached instances (т.е. манипулировать одним Entity в нескольких транзакциях/сессиях).

Implementing equals() and hashCode()

Оптимально использовать уникальные бизнес-поля, но обычно таких нет, и чаще всего используются PK с ограничением, что он может быть null у новых объектов, и нельзя объекты сравнивать через equals() and hashCode() в бизнес-логике (например, тестах).

equals() and hashcode() when using JPA and Hibernate


question Почему над AbstractBaseEntity стоит @Access(AccessType.FIELD) ? Почему при запросе user.id нам не нужно вытаскивать его из базы?

AccessType.FIELD делает доступ в AbstractBaseEntity и всех классах-наследниках по полям. При загрузке Meal Hibernate на основе поля meal.user_id делает ленивую прокcи к User, у которой нет ничего, кроме id.

Apply 5_4_HW4_optional.patch

Apply 5_5_HW4_optional_refactoring.patch

How do you assert that a certain exception is thrown in JUnit 4 tests?

Занятие 5:

Раскрасил лог (в Spring Boot по умолчанию он тоже colored)

Apply 5_6_log_colored.patch

Apply 5_7_profiles_connection_pool.patch

  • SLF4JBridgeHandler перенес в профиль postgres (если логировать драйвер не нужно, то и он не нужен)
  • Галочка в XML-профиле влияет только на отображение в IDEA и никак не влияет на выполнение кода.
  • Profiles.ACTIVE_DB задает активный профиль базы (postgres/hsqldb)
  • Profiles.REPOSITORY_IMPLEMENTATION определяет реализацию репозитория при запуске приложения (для тестов задаются через @ActiveProfiles).

Для переключения на HSQLDB необходимо:

  • поменять в окне Maven Projects профиль (Profiles) - выключить postgres, включить hsqldb - и сделать Reimport All Maven Projects (1-я кнопка)
  • поменять Profiles.ACTIVE_DB = HSQLDB
  • почистить проект mvn clean (фаза clean не выполняется автоматически, чтобы каждый раз не перекомпилировать весь проект)

Для корректного отображения неактивного профиля в IDEA проверьте флаг Inactive profile highlighting и сделайте проекту clean

image

Вопрос: почему после этого патча не поднимается Spring при запуске приложения в Tomcat? (будем чинить в ДЗ, п.6)

Автоматический выбор профиля базы: ActiveProfilesResolver

Apply 5_8_profile_resolver.patch

Сделал автоматический выбор профиля базы при запуске приложения (тестов) в зависимости от присутствия драйвера базы в classpath (Profiles.getActiveDbProfile())

video Александр Колесников - JDBC Pools Battle (ссылка на выводы)

BoneCP to be deprecated

Apply 5_9_spring_data_jpa.patch

  • Переименовал классы Proxy на более адекватные Crud, убрал Impl
  • В spring-framework-bom мы уже задали версию Spring. Убрал из остальных зависимостей.
  • В spring-data-jpa 2.x поменялся интерфейс: T CrudRepository.findOne(ID id) -> Optional<T> CrudRepository findById(ID id)
  • Не стал переопределять в CrudUserRepository методы JpaRepository (для явного указания всех используемых методов). Обычно этого не делают.

question Какой паттерн проектирования применён в классе DataJpaUserRepository (декоратор/адаптер/другой)?:

Вопрос интересный:) Ближе всего к адаптеру, но скорее композиция с делегированием. Мы просто используем для нашей реализации возможности data-jpa: CrudUserRepository. Делегат интерфейсов не меняет, а прокси похож на делегата, но служит для неявной подмены (часто прямо в рантайм). См. ПАТТЕРНЫ ПРОЕКТИРОВАНИЯ

Apply 5_10_spring_cache.patch

question Ваши вопросы

В spring-petclinic DataJpa реализована без дополнительных классов. В таком виде как у них, spring data смотрится, конечно, намного лаконичней других реализаций, но у нас получилось вдвое больше кода, чем с тем же jpa или jdbc. Плюс только пожалуй в том, что query находятся прямо в репозитории, а не где-то там в другом пакете. Так что получается, spring data лучше подходит для простейших crud без всяких "фишек"? или в чем его достоинство для больших и сложных проектов?

Достоинства DATA-JPA по сравнению, например, с JPA: есть типизация, готовые реализации типовых методов CRUD, а также paging, data-common. Мы можем переключить реализацию JPA, например, на mongoDb (PagingAndSortingRepository, от которого наследуется JpaRepository, находится в spring-data-common). Соответственно, его методы будут поддерживаться всеми реализациями spring-data-common (JPA - одна из них) и пр. Подробнее о них есть в видео Spring Data – новый взгляд на persistence. Дополнительное проксирование в DATA-JPA - моя "фишка" для устранения минусов этого фреймворка: невозможность дебага, привязка к интерфейсу JpaRepository, перенос логики Repository в слой сервисов. Для большого приложения выигрыш этого стоит. Для небольших (тестовых) приложений (например выпускного) дополнительных классов лучше не делать.

Почему мы для InMemory не сделали отдельного профиля? Почему их не удалить вообще?

Реализация InMemory является примером, как в test делать подмену контекста. Для них сделали отдельный inmemory.xml, и запускаемый проект ничего не должен о них знать. У нас учебный проект, в котором 4 реализации репозиториев, в реальном такого не будет.

А как делать транзакционность для реализации jdbc?

Будем делать на следующем уроке.


  • 1: Имплементировать DataJpaMealRepository.
  • 2: Разделить реализации Repository по профилям Spring: jdbc, jpa, datajpa (общее в профилях можно объединять, например, <beans profile="datajpa,jpa">).
    • 2.1: Профили выбора DB (postgres/hsqldb) и реализации репозитория (jdbc/datajpa/jpa) независимы друг от друга, и при запуске приложения (тестов) нужно задать тот, и другой.
    • 2.2: Для интеграции с IDEA не забудьте выставить в spring-db.xml справа вверху в Change Profiles... профили, например, datajpa, postgres.
    • 2.3: Общие части для всех в spring-db.xml можно оставить как есть без профилей вверху файла (до первого <beans profile= !!!).
  • 3: Сделать тесты всех реализаций (jdbc, jpa, datajpa) через наследование (без дублирования).
    • 3.1 сделать один базовый класс для MealServiceTest и UserServiceTest.
    • 3.2 сводку по времени выполнения тестов также сделать для user
  • 4: Проверить запуск всех тестов: mvn test (в IDEA Maven Lifecycle - test, кнопку skipTest отжать).

Optional

  • 5: Разделить JdbcMealRepository для HSQLDB (она не умеет работать с Java8 Time API) и Postgres через @Profile (для Postgres оставить LocalDateTime).
    • Цель задания - потренироваться с паттерном "шаблонный метод" и профилями Spring. Какие бины Spring попадут в контекст зависит от выставления активных профилей при запуске (@ActiveProfiles в тестах) и конфигурации, где задаются бины для каждого профиля. Абстрактные классы не создаются и в контекст не попадают.
    • После выполнения разделения на основе профилей, можно предложить решение проще.
  • 6: Починить MealServlet и использовать в SpringMain реализацию DB: добавить профили. Попробуйте поднять Spring контекст без использования spring.profiles.active.
  • 7: Сделать и протестировать в сервисах методы (тесты и реализация - только для DataJpa):
    • 7.1: достать по id пользователя вместе с его едой
    • 7.2: достать по id и userId еду вместе с пользователем
    • 7.3: обращения к DB сделать в одной транзакции (можно сделать разные варианты). Java Persistence/OneToMany

error Типичные ошибки и подсказки по реализации

  • 1: Для того, чтобы не запускались родительские классы тестов, нужно сделать их abstract.
  • 2: В реализациях JdbcMealRepository код не должен дублироваться. Если вы возвращаете тип Object, посмотрите в сторону дженериков.
  • 3: В MealServlet/SpringMain в момент setActiveProfiles контекст спринга еще не должен быть инициализирован, иначе выставление профиля уже ни на что не повлияет.
  • 4: Если у метода нет реализации, то стандартно бросается UnsupportedOperationException. Для уменьшения количества кода при реализации Optional (п. 7, только DataJpa) попробуйте сделать default метод в интерфейсе.
  • 5: В Data-Jpa метод для ссылки на entity (аналог em.getReference) - T getOne(ID id)
  • 6: Проверьте, что в DataJpaMealRepository все обращения к DB выполняются в одной транзакции.
  • 7: Для 7.1 достать по id пользователя вместе с его едой я в User добавил List<Meal> meals. Учесть, что у юзера может отсутствовать еда. Ordering a join fetched collection in JPA using JPQL/HQL
  • 8: Проверьте, что все тесты запускаются из Maven (имена классов тестов удовлетворяют соглашению) и итоги тестов класса выводятся корректно (не копятся). По умолчанию maven-surefire-plugin включает в тесты классы, заканчивающиеся на Test.
  • 9: @ActiveProfiles принимает в качестве параметра строку либо массив строк. В тестах можно задавать несколько @ActiveProfiles в разных классах, они суммируются
  • 10: <beans profile= в конфигурации контекста должны находиться после всех остальных объявлений.