-
Notifications
You must be signed in to change notification settings - Fork 35
Lesson 5
Онлайн проект Topjava
Для запуска Tomcat под JDK11 проверьте переменную окружения
JAVA_HOME
- Добавил javax зависимости
- Сделал создание коллекций через фабричные методы
List.of - Как пример в
InMemoryMealRepositoryImplиспользовал local variable type inferencevar.
- Validate by RegExp
- Working with JPA Entity Objects
- Java Persistence/Relationships
- Использование ThreadLocal переменных
- Merge vs Persist
- Видео: работа в ZK с OpenJPA (в чем Hibernate хуже)
- Паттерн- открытие транзакции в фильтре и почему это bad-practice
- Sequence Strategies
- SequenceGenerator/IdentityGenerator in PostgreSql
EntityManagerэто по сути прокси обертка над Hibernate Session, которая создается каждый раз при открытии транзакции.
- Дополнительно (ни разу не сталкивался): еще есть редкий случай ручного управления
@PersistenceContext(type = PersistenceContextType.EXTENDED), когда он используется в нескольних транзакциях (long-running session or session-per-conversation).
- При сравнении еды тесты падают, тк Hibernate делает ленивую обертку к
userи если происходит обращение к любому его полю кроме id вне транзакции бросаетсяLazyInitializationException. По логике приложения полеuserв еде не нужно и мы не будем его отдавать наружу: в тестах исключаемuserиз сравнения.- IDEA ругается на BETWEEN, в
Mealдобавил@SuppressWarnings("JpaQlInspection"). Other warnings- Поменял реализацию
JpaMealRepositoryImpl.get()(вместо@NamedQuery), реализация стали проще
- Из за Hibernate bug with proxy initialization when using
AccessType.FIELDвJpaMealRepositoryImpl.get()делался дополнительный запрос в базу для инициализации проксиUserи мы делали хак: доступ к полюAbstractBaseEntity.idчерезAccessType.PROPERTY. С версии5.2.13.Finalзагрузкой прокси при обращении кidуправляется флагомJPA_PROXY_COMPLIANCE(по умолчанию запрос не делается)- Which is better, field or property access?
- Поправил
equals()с учетом Lazy проксирования
Переопределять
equals()/hashCode()необходимо, если
- использовать entity в
Set(рекомендовано для many ассоциаций), либо как ключи вHashMap- использовать reattachment of detached instances (те манипулировать одним Entity в нескольких транзакциях/сессиях).
Оптимально использовать уникальные бизнес поля, но обычно таких нет, и чаще всего, используются PK с ограничением, что он может быть
nullу новых объектов и нельзя объекты сравнивать черезequals() and hashCode()в бизнес-логике (например тестах).
Почему над
AbstractBaseEntityстоит@Access(AccessType.FIELD)? Почему при запросеuser.idнам не нужно вытаскивать его из базы?
AccessType.FIELD делает доступ в AbstractBaseEntity и всех классах-наследниках по полям. При загрузке Meal Hibernate на основе поля meal.user_id делает ленивую прокcи к User, у которой нет ничего, кроме id.
- Hibernate 5.2.x already include Java 8 date and time types (JSR-310)
- Stopwatch
- Добавил сводку "имя теста - время выполнения" в конце класса
3. Транзакции
- Галочка в XML профиле влияет только на отображение в IDEA и никак на выполнение кода.
tomcat-jdbcсоscope=providedне работает в Tomcat 7.x, поставьте Tomcat 8.xProfiles.ACTIVE_DBзадает активный профиль базы (postgres/hsqldb)Profiles.REPOSITORY_IMPLEMENTATIONопределяет реализацию репозитория при запуске приложения (для тестов задаются через@ActiveProfiles).
Для переключения на HSQLDB необходимо:
- поменять в окне Maven Projects профиль (Profiles) на
hsqldbи сделатьReimport All Maven Projects(1я кнопка)- поменять
Profiles.ACTIVE_DB = HSQLDB- почистить проект
mvn clean(фазаcleanне выполняется автоматически, чтобы каждый раз не перекомпилировать весь проект)
Для корректного отображения неактивного профиля в IDEA проверьте флаг Inactive profile highlighting и сделайте проекту clean

Вопрос: почему после этого патча не поднимется Spring при запуске приложения в Tomcat? (будем чинить в ДЗ п.6)
Автоматический выбор профиля базы: ActiveProfilesResolver
Сделал автоматический выбор профиля базы при запуске приложения (тестов) в зависимости от присутствия драйвера базы в classpath (
Profiles.getActiveDbProfile())
- Выбор реализации пула коннектов: BoneCP, Commons Database Connection Pooling, HikariCP
- Самый быстрый пул соединений на java (читаем комменты)
- Tomcat pool
- Переименовал классы Proxy на более адекватные Crud
- В
spring-framework-bomмы уже задали версию Spring. Убрал из остальных зависимостей.- В spring-data-jpa 2.x поменялся интерфейс:
T CrudRepository.findOne(ID id)->Optional<T> CrudRepository findById(ID id)
- Spring Data JPA
- Замена AbstractDAO: JPA Repositories
- Разрешение зависимостей: Maven BOM [Bill Of Materials] Dependency
- Делегирование (в конце статьи)
- Getting started with Spring Data JPA
- Query methods
- Spring Data – новый взгляд на persistence (JeeConf)
- Евгений Борисов — Spring Data? Да, та!
- Ресурсы:
Вопросы:
Зачем мы переопределяем
@Override Meal saveвCrudUserRepository. Без этого все работает.
Можно не переопределять. Сделал только для явного указания всех используемых методов из наследуемого CrudRepository
Какой паттерн проектирования применён в классе DataJpaUserRepositoryImpl (декоратор/адаптер/другой)?:
Вопрос интересный:) Я бы назвал это композицией с делегированием. Если бы стояла бизнес задача преобразовать UserRepository в CrudUserRepository то это был бы адаптер (у нас нет бизнес задачи, CrudUserRepository - внутреняя фича нашей реализации data-jpa)
Делегат интерфейсов не меняет, а прокси похож на делегата, но служит неявной подмены (часто прямо в рантайм). См. ПАТТЕРНЫ
ПРОЕКТИРОВАНИЯ
7. Spring кэш
- Сделал миграцию на Ehcache 3.x, compatibile with javax.cache API (JSR-107)
- В
UserServiceTest.setUpвместо вызова методаUserService.evictCacheсделал очистку програмно черезCacheManager
- Кеширование в Spring Framework
- Дополнительно:
Поправил версию JDK в .travis.yml
В 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: Имплементировать
DataJpaMealRepositoryImpl - 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=)!!.
- 2.1: Профили выбора DB (
- 3: Сделать тесты всех реализаций (
jdbc, jpa, datajpa) через наследование (без дублирования)- 3.1 сделать один базовый класс для
MealServiceTestиUserServiceTest. - 3.2 сводку по времени выполнения тестов также сделать для
user
- 3.1 сделать один базовый класс для
- 4: Запустить все тесты:
mvn test(в IDEA Maven Lifecycle - test, кнопку skipTest отжать)
- 5: Разделить
JdbcMealRepositoryImplдля HSQLDB (она не умеет работать с Java8 Time API) и Postgres через@Profile(для Postgres оставитьLocalDateTime). Цель задания - потренироваться с паттерном шаблонный метод и профилями Spring. Бины Spring мы разделяем (фильтруем) по разным профилям с помощьюbeans profileв xml конфигурации и@Profile(те мы конфигурируем, какие бины попадут в контекст Spring в зависимости от активных профилей приложения). Абстрактные классы не создаются и в контекст не попадают. Профили, заданные в@Profileпересекаются с активными профилями приложения: если пересечение есть, то бин включается в контекст (cм. реализацию@Profileи в нейProfileCondition, можно подебажить). Например при сконфигуренном@Profile({"postgres","jdbc"})бин попадет в контекст, если в профилях запущенного приложения есть хотя бы один из них (например "jdbc"). После выполнения разделения можно предложить решение проще. - 6: Починить
MealServletи использовать вSpringMainреализацию DB: добавить профили. Попробуйте поднять Spring контекст без использованияspring.profiles.active. - 7: Сделать и протестировать в сервисах методы (тесты и реализация только для
DataJpa)- 7.1: достать по id пользователя вместе с его едой
- 7.2: достать по id еду вместе с пользователем
- 7.3: обращения к DB сделать в одной транзакции (можно сделать разные варианты). Java Persistence/OneToMany
- 1: Для того, чтобы не запускались родительские классы тестов нужно сделать их
abstract - 2: В реализациях
JdbcMealRepositoryкод не должен дублироваться. Если вы возвращаете типObject, посмотрите в сторону дженериков. - 3: В
MealServlet/SpringMainв моментsetActiveProfilesконтекст спринга еще не должен быть инициализирован, иначе выставление профиля уже ничего не будет делать. - 4: Если у метода нет реализации, то стандартно бросается
UnsupportedOperationException. - 5: Для уменьшения количества кода при реализации Optional (п. 7, только
DataJpa) попробуйте сделатьdefaultметод в интерфейсе - 6: В Data-Jpa метод для ссылки на entity (аналог
em.getReference) :T getOne(ID id) - 7: Проверьте, что в
DataJpaMealRepositoryImplвсе обращения к DB выполняются в одной транзакции - 8: Для
достать по id пользователя вместе с его едойя вUserдобавилList<Meal> meals - 9: Проверьте, что все тесты запускаются из Maven (имена классов тестов удовлетворяют соглашению) и итоги тестов класса выводятся корректно (не копятся)
- 10:
@ActiveProfilesпринимает в качестве параметра строку, либо массив строк. В тестах можно задавать несколько@ActiveProfilesв разных классах, они суммируются - 11: В релизации 7.1 учесть, что у юзера может отсутствовать еда
- 12: Ordering a join fetched collection in JPA using JPQL/HQL
- 13:
<beans profile=в конфигурации контекста должны находиться после всех остальных объявлений. - 14: По умолчанию maven-surefire-plugin включает в тесты классы, заканчивающиеся на Test.
Разбор домашнего задания HW4
Подсказки по HW05