Skip to content
Vladimir edited this page Feb 9, 2019 · 1 revision

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

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

SpringMain, InMemoryAdminRestControllerTest, InMemoryAdminRestControllerSpringTest починим в патче 4_5_create_inmemory_test_ctx.patch (видео 4)

Apply 4_1_HW3.patch

  • В JdbcUserRepositoryImpl поменял MapSqlParameterSource на BeanPropertySqlParameterSource (поля для вставки определяются через отражение в бине и метаданные в SQL запросе). В JdbcMealRepositoryImpl остается MapSqlParameterSource, т.к. в отсутствует Meal.userId. См. дополнительно CombinedSqlParameterSource
  • Новый Postgres драйвер поддерживает Java 8 Date and Time. Преобразования c Timestamp уже не нужны.
  • В meals добавил составной индекс INDEX meals_unique_user_datetime_idx ON meals(user_id, date_time) для повышения скорости запросов по этим полям

Примечание: в ответе на Why is SELECT * considered harmful? есть случаи, когда она допустима (наш случай):

When "*" means "a row"

Apply 4_2_HW3_optional.patch

Удалил лишние MealsUtil.MEALS

Занятие 4:

Apply 4_3_improve_code.patch

Для пояснения материала видео следал проверку предусловий Objects.requireNonNull и Assert.notNull. В реальном проекте везде используются один подход.

Apply 4_4_init_and_populate_db.patch

Apply 4_5_create_inmemory_test_ctx.patch

Переименовал mock.xml в inmemory.xml

Entity- класс (объект Java), который в ORM маппится в таблицу DB.

Apply 4_6_add_jpa.patch

  • Внимание: при настройке JPA в IDEA НЕ скачивайте библиотеку javaee.jar (и любую другую). Все зависимости в проект попадают только через Maven.
  • Тесты и приложение ломаются. MealServiceTest починится после выполнения HW04 (JpaMealRepositoryImpl)
  • Если вы используете Java 9, то возникают проблемы с JAXBException (пакет java.xml.bind). См. решение

Apply 4_7_add_named_query_and_transaction.patch

Справочник:

Apply 4_8_add_hsqldb.patch

  • ВНИМАНИЕ: патч меняет postgres.properties
  • IDEA может ${jdbc.initLocation} подчеркивать красным - тупит...

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

Есть несколько аналогичных "встроенных" баз данных. H2, HSQLDB, Derby, SQLite. Почему был выбран HSQLDB?

Просто с ней приходилось работать. HSQLDB и H2 наиболее популярны, в новом курсе по spring-boot планирую использовать H2. Здесь интересное краткое описание встраиваемых баз данных в Java. В HSQLDB нет репликаций, кластеризации и объем данным ограничен несколькими TB. Для большого количества приложений она подходит и для продакшена. См.

Чистого JPA не существует, т.е. это всего лишь интерфейс, спецификация? Говорим JPA, подразумеваем какой-то ORM фрэймворк? А что тогда используют чистый jdbc, Spring-jdbc, MyBatis? MyBatis не реализует JPA?

ORM это технология связывания БД и объектов приложения, а JPA - это JavaEE спецификация (API) этой технологии. Реализации JPA - Hibernate, OpenJPA, EclipceLink, но, например, Hibernate может работать по собственному API (без JPA, которая появиласть позже). Spring-JDBC, MyBatis, JDBI не реализуют JPA, это обертки к JDBC. Все ORM и JPA также реализованы поверх JDBC.

В зависимостях maven hibernate-entitymanager тянет за собой jboss-logging. Как будет происходить логгирование?

How do you configure logging in Hibernate 4 to use SLF4J: в нашем проекте автоматически подхватывается logback-classic.

В чем преимущество Hibernate ?

Hibernate (как любая ORM) реализует маппинг таблиц в объекты Java. Когда мы добавим роли к пользователю вы увидете, насколько код будет проще, чем в jdbc. Также см. 5 Reasons to Use JPA / Hibernate

Чем отличается @Column(nullable = false) от @NotNull и есть ли необходимость указывать обе аннотации ?

@Column(nullable = false) это атрибуты колонки таблицы базы. @NotNull - это валидация, которая происходит в приложении перед вставкой в базу. Если колонка ненулевая, то NOT NULL объязательна. Валидация- опциональна. Также см. @NotNull vs @Column(nullable = false)

почему мы в в бине entityManagerFactory не указали диалект базы данных?

Он автоматически определяется из DataSource драйвера

В чем разница между persist и merge

Подробный ответ со Stackovwrflow с объяснением разницы. Упрощенно:

  • merge, в отличие от persist, если entity нет в текущей сессии, делает запрос в базу данных
  • entity, переданный в merge не меняется. Нужно использовать возвращаемый результат

em.merge - при отсутствии старой записи (несуществующее id) создает новую. Те в JpaUserRepositoryImpl нарушается логика

В Hibernate есть такая бага: https://hibernate.atlassian.net/browse/HHH-1661

Если это действительно наш критичный бизнес кейс (например с многопоточным удалением entity) нужно искать варианты обходного решения. Если же это результат неверного запроса, то, мое мнение, можно это оставить как есть.

Почему в проекте транзакционность сделана в слое репозитория, а не сервиса? Транзакциями удобнее пользоваться на слое сервисов, так как здесь реализуется бизнес логика и бывает нужно делать несколько операций в одной транзакции.

С классической точки зрения все транзакции действительно объявляются на уровне сервиса. Мы будем использовать в логике сервиса несколько запросов и тогда сделаем дополнительную транзакцию на методе сервисе. Новая транзакция при этом не создается (по умолчанию используется Propagation.REQUIRED, который поддерживают существующую), поэтому несколько @Transactional аннотаций ведут себя как одна. Я использую подход spring-data-jpa (будет на следующем занятии): в репозитории транзакции объявлять удобно, тк не надо думать о них в сервисах.


hw Домашнее задание HW4

Optional

  • 3: Добавить в тесты MealServiceTest функциональность @Rule:
    • 3.1: проверку Exception
    • 3.2: вывод в лог времени выполнения каждого теста
    • 3.3: вывод сводки в конце класса: имя теста - время выполнения
  • JUnit @Rules
  • замена ExpectedException

error Подсказки по HW4

  • 1: Тк. JPQL работает с объектами мы не можем использовать userId для сохранения. Можно сделать например так:

     User ref = em.getReference(User.class, userId);
     meal.setUser(ref);
    

    При этом от User нам нужет только id. Создается lazy прокси над id, которая обращается к базе при запросе любого поля. Т.е. у нас запроса в базу за юзером не будет- проверьте по логам Hibernate

Внимание: проверять запросы Hibernate нужно через run. Если делаете debug и брекпойнт, то могут делаться лишние запросы к базе (дебаггер дергает toString)

  • 2: В JPQL запросах можно писать: m.user.id=:userId
  • 3: При реализации JpaMealRepositoryImpl предпочтительно не использовать try-catch в логике реализации. Но если очень хочется, то ловить только специфичекские эксепшены (пр. NoResultException), чтобы, например, при отсутствии коннекта к базе приложение отвечало адекватно.
  • 4: Мы будем смотреть генерацию db скриптов из модели, для корректной генерации нужно в Meal добавить uniqueConstraints
  • 5: При записи в базу через namedQuery валидация ентити не работает, только валидация в бд
  • 6: Результат AssertionError печатает результаты через toString, который может не совпадать с полями сравнения.
  • 7: Если нашему приложению Meal.user не требуется, не следует включать его в тесты. В следующем уроке мы потренируемся разными способами доставать зависимости Meal.user и User.meals
  • 8: Старые версии IDEA тупят по поводу проверки BETWEEN. Обновитесь либо не обращайте внимания.

Новая информация плохо оседает в голове, когда дается в виде патчей, поэтому, чтобы она стала "твоей" нужно еще раз проделать это самостоятельно. Домашнее задание на этом уроке небольшое, а полученных знаний уже достаточно, чтобы после его выполнения начинать делать выпускной проект, сделанный на нашем стеке.

Успехов в выполнении!

Clone this wiki locally