ОглавлениеCore
Скачать 1.53 Mb.
|
READ_ONLY: Используется только для сущностей, которые никогда не изменяются (будет выброшено исключение, если попытаться обновить такую сущность). Просто и производительно. Подходит для некоторых статических данных, которые не меняются. • NONSTRICT_READ_WRITE: Кеш обновляется после совершения транзакции, которая изменила данные в БД и закоммитила их. Таким образом, строгая согласованность не гарантируется, и существует небольшое временное окно между обновлением данных в БД и обновлением тех же данных в кеше, во время которого параллельная транзакция может получить из кеша устаревшие данные. • READ_WRITE: Гарантирует строгую согласованность, которая достигается за счет «мягких» блокировок: когда обновляется кешированная сущность, на нее накладывается мягкая блокировка, которая снимается после коммита транзакции. Все параллельные транзакции, которые пытаются получить доступ к записям в кеше с наложенной мягкой блокировкой, не смогут их прочитать или записать и отправят запрос в БД. Ehcache использует эту стратегию по умолчанию. • TRANSACTIONAL: полноценное разделение транзакций. Каждая сессия и каждая транзакция видят объекты, словно они работали с ними последовательно одна транзакция за другой. Плата за это – блокировки и потеря производительности. Что такое JPQL/HQL и чем он отличается от SQL? Hibernate Query Language (HQL) и Java Persistence Query Language (JPQL) являются объектно-ориентированными языками запросов, схожими по природе с SQL. JPQL – это подмножество HQL. JPQL – это язык запросов, практически такой же, как SQL, но вместо имен и колонок таблиц базы данных использует имена классов Entity и их атрибуты. В качестве параметров запросов используются типы данных атрибутов Entity, а не полей баз данных. В отличие от SQL в JPQL есть автоматический полиморфизм, то есть каждый запрос к Entity возвращает не только объекты этого Entity, но и объекты всех его классов-потомков, независимо от стратегии наследования. В JPA запрос представлен в виде javax.persistence.Query или javax.persistence.TypedQuery, полученных из EntityManager. В Hibernate HQL-запрос представлен org.hibernate.query.Query, полученный из Session. Если HQL является именованным запросом, то будет использоваться Session#getNamedQuery, в противном случае требуется Session#createQuery. Что такое Criteria API и для чего он используется? Начиная с версии 5.2, Hibernate Criteria API объявлен deprecated. Вместо него рекомендуется использовать JPA Criteria API. JPA Criteria API – это актуальный API, используемый только для выборки (select) сущностей из БД в более объектно-ориентированном стиле. Основные преимущества JPA Criteria API: • ошибки могут быть обнаружены во время компиляции; • позволяет динамически формировать запросы на этапе выполнения приложения. Основные недостатки: • нет контроля над запросом, сложно отловить ошибку; • влияет на производительность, множество классов. Для динамических запросов фрагменты кода создаются во время выполнения, поэтому JPA Criteria API является предпочтительней. Н екоторые области применения Criteria API: • поддерживает проекцию, которую можно использовать для агрегатных функций вроде sum(), min(), max() и т. д.; • может использовать ProjectionList для извлечения данных только из выбранных колонок; • может быть использована для join запросов с помощью соединения нескольких таблиц, используя методы createAlias(), setFetchMode() и setProjection(); • поддерживает выборку результатов согласно условиям (ограничениям). Для этого используется метод add(), с помощью которого добавляются ограничения (Restrictions). • позволяет добавлять порядок (сортировку) к результату с помощью метода addOrder(). Расскажите про проблему N+1 Select и путях ее решения Проблема N+1 запросов возникает, когда получение данных из БД выполняется за N дополнительных SQL-запросов для извлечения тех же данных, которые могли быть получены при выполнении основного SQL-запроса. 1. JOIN FETCH И при FetchType.EAGER, и при FetchType.LAZY поможет JPQL-запрос с JOIN FETCH. Опцию «FETCH» можно использовать в JOIN (INNER JOIN или LEFT JOIN) для выборки связанных объектов в одном запросе вместо дополнительных запросов для каждого доступа к ленивым полям объекта. Лучший вариант решения для простых запросов (1-3 уровня вложенности связанных объектов). select pc from PostComment pc join fetch pc.post p 2. EntityGraph Если нужно получить много данных через jpql-запрос, лучше всего использовать EntityGraph. 3. @Fetch(FetchMode.SUBSELECT) Аннотация Hibernate. Можно использовать только с коллекциями. Будет сделан один sql- запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям-коллекциям, то выполнится еще один запрос для получения связанных коллекций: @Fetch(value = FetchMode.SUBSELECT) private Set 4. Batch fetching Аннотация Hibernate, в JPA ее нет. Указывается над классом сущности или над полем коллекции с ленивой загрузкой. Будет сделан один sql-запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям- коллекциям, то выполнится еще один запрос для получения связанных коллекций. Количество загружаемых сущностей указывается в аннотации. @BatchSize(size=5) private Set 5. HibernateSpecificMapping, SqlResultSetMapping Рекомендуется использовать для нативных запросов. Что такое EntityGraph? Как и для чего их использовать? Основная цель JPA Entity Graph – улучшить производительность в рантайме при загрузке базовых полей сущности и связанных сущностей и коллекций. Hibernate загружает весь граф в одном SELECT-запросе, то есть все указанные связи от нужной сущности. Если необходимо загрузить дополнительные сущности, находящиеся в связанных сущностях, используется Subgraph. EntityGraph можно определить с помощью аннотации @NamedEntityGraph для Entity, она определяет уникальное имя и список атрибутов (attributeNodes), которые должны быть загружены с использованеим entityManager из JPA API: EntityGraph entityGraph = entityManager.createEntityGraph(Post.class); entityGraph.addAttributeNodes("subject"); entityGraph.addAttributeNodes("user"); entityGraph.addSubgraph("comments").addAttributeNodes("user"); JPA определяет два свойства или подсказки, с помощью которых Hibernate может выбирать стратегию извлечения графа сущностей во время выполнения: • fetchgraph – все атрибуты, перечисленные в EntityGraph, меняют fetchType на EAGER, все остальные – на LAZY; • loadgraph – все атрибуты, перечисленные в EntityGraph, меняют fetchType на EAGER, все остальные сохраняют свой fetchType. С помощью NamedSubgraph можно изменить fetchType вложенных объектов Entity. Загрузить EntityGraph можно тремя способами: 1. Используя перегруженный метод find(), который принимает Map с настройками EntityGraph. 2. Используя JPQL и передав подсказку через setHint(). 3. С помощью Criteria API. Мемоизация Memoization – вариант кеширования, заключающийся в том, что для функции создается таблица результатов. Результат функции, вычисленной при определенных значениях параметров, заносится в эту таблицу. В дальнейшем результат берется из данной таблицы. Эта техника позволяет за счет использования дополнительной памяти ускорить работу программы. Можно применить только к функциям, которые являются: • детерминированными (т. е. при одном и том же наборе параметров функции должны возвращать одинаковое значение); • без побочных эффектов (т. е. не должны влиять на состояние системы). В Java наиболее подходящей кандидатурой на роль хранилища является интерфейс Map. Сложность операций get,put,contains равна O(1). Это позволяет гарантировать ограничение задержки при выполнении мемоизации. Мемоизация реализована в библиотеке ehcache. Spring Что такое инверсия контроля (IoC) и внедрение зависимостей (DI)? Как эти принципы реализованы в Spring? Inversion of Control – подход, который позволяет конфигурировать и управлять объектами Java с помощью рефлексии. Вместо ручного внедрения зависимостей фреймворк забирает ответственность за это через IoC-контейнер. Контейнер отвечает за управление жизненным циклом объектов: создание объектов, вызов методов инициализации и конфигурирование объектов через связывание их между собой. Объекты, создаваемые контейнером, называются beans. Конфигурирование контейнера осуществляется через внедрение аннотаций, но есть возможность, по старинке, загрузить XML-файлы, содержащие определение bean’ов и предоставляющие информацию, необходимую для создания bean’ов. Dependency Injection является одним из способов реализации принципа IoC в Spring. Это шаблон проектирования, в котором контейнер передает экземпляры объектов по их типу другим объектам с помощью конструктора или метода класса (setter), что позволяет писать слабосвязный код. Что такое IoC контейнер? В среде Spring IoC-контейнер представлен интерфейсом ApplicationContext, который является оберткой над BeanFactory, предоставляющей дополнительные возможности, например AOP и транзакции. Интерфейс BeanFactory предоставляет фабрику для бинов, которая в то же время является IoC-контейнером приложения. Управление бинами основано на конфигурации (аннотации или xml). Контейнер создает объекты на основе конфигураций и управляет их жизненным циклом от создания объекта до уничтожения. Расскажите про ApplicationContext и BeanFactory, чем отличаются? В каких случаях что стоит использовать? ApplicationContext является наследником BeanFactory и полностью реализует его функционал, добавляя больше специфических enterprise-функций. Может работать с бинами всех скоупов. BeanFactory – это фактический контейнер, который создает, настраивает и управляет рядом bean-компонентов. Эти бины обычно взаимодействуют друг с другом и имеют зависимости между собой. Эти зависимости отражены в данных конфигурации, используемых BeanFactory. Может работать с бинами singleton и prototype. BeanFactory обычно используется тогда, когда ресурсы ограничены (мобильные устройства), так как он легче по сравнению с ApplicationContext. Поэтому, если ресурсы не сильно ограничены, то лучше использовать ApplicationContext. ApplicationContext загружает все бины при запуске, а BeanFactory по требованию. Расскажите про аннотацию @Bean? Аннотация @Bean используется для указания того, что метод создает, настраивает и инициализирует новый объект, управляемый IoC-контейнером. Такие методы можно использовать как в классах с аннотацией @Configuration, так и в классах с аннотацией @Component (или ее наследниках). Имеет следующие свойства: • destroyMethod, initMethod – варианты переопределения методов инициализации и удаления бина при указании их имен в аннотации; • name – имя бина, по умолчанию именем бина является имя метода; • value – алиас для name(). Расскажите про аннотацию @Component? @Component используется для указания класса в качестве компонента Spring. Такой класс будет сконфигурирован как spring Bean. Чем отличаются аннотации @Bean и @Component? @Bean ставится над методом и позволяет добавить bean, уже реализованного сторонней библиотекой класса, в контейнер, а @Component используется для указания класса, написанного программистом. Расскажите про аннотации @Service и @Repository. Чем они отличаются? @Repository указывает, что класс используется для работы с поиском, получением и хранением данных. Аннотация может использоваться для реализации шаблона DАО. @Service указывает, что класс является сервисом для реализации бизнес-логики. @Repository, @Service, @Controller и @Configuration являются алиасами @Component, их также называют стереотипными аннотациями. Задача @Repository заключается в том, чтобы отлавливать определенные исключения персистентности и пробрасывать их как одно непроверенное исключение Spring Framework. Для этого в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor. Расскажите про аннотацию @Autowired @Autowired – автоматическое внедрение подходящего бина: 1. Контейнер определяет тип объекта для внедрения. 2. Контейнер ищет соответствующий тип бина в контексте (он же контейнер). 3. Если есть несколько кандидатов и один из них помечен как @Primary, то внедряется он. 4. Если используется @Qualifier, то контейнер будет использовать информацию из @Qualifier, чтобы понять, какой компонент внедрять. 5. В противном случае контейнер внедрит бин, основываясь на его имени или ID. 6. Если ни один из способов не сработал, то будет выброшено исключение. Контейнер обрабатывает DI с помощью AutowiredAnnotationBeanPostProcessor. В связи с этим аннотация не может быть использована ни в одном BeanFactoryPP или BeanPP. В аннотации есть один параметр required = true/fals. Он указывает, обязательно ли делать DI. По умолчанию true. Либо можно не выбрасывать исключение, а оставить поле c null, если нужный бин не был найден – false. При циклической зависимости, когда объекты ссылаются друг на друга, нельзя ставить над конструктором. Однако при внедрении прямо в поля не нужно предоставлять прямого способа создания экземпляра класса со всеми необходимыми зависимостями. Это означает, что: • существует способ (через вызов конструктора по умолчанию) создать объект с использованием new в состоянии, когда ему не хватает некоторых из его обязательных зависимостей, и использование приведет к NullPointerException; • такой класс не может быть использован вне DI-контейнеров (тесты, другие модули) и нет способа кроме рефлексии предоставить ему необходимые зависимости; • неизменность; В отличие от способа с использованием конструктора внедрение через поля не может использоваться для присвоения зависимостей final-полям, что приводит к тому, что объекты становятся изменяемыми. Расскажите про аннотацию @Resource @Resource (аннотация java) пытается получить зависимость: по имени, по типу, затем по описанию (Qualifier). Имя извлекается из имени аннотируемого сеттера или поля либо берется из параметра name. @Resource //По умолчанию поиск бина с именем "context" private ApplicationContext context; @Resource(name="greetingService") //Поиск бина с именем "greetingService" public void setGreetingService(GreetingService service) { this.greetingService = service; } Отличие от @Autowired: • ищет бин сначала по имени, а потом по типу; • не нужна дополнительная аннотация для указания имени конкретного бина; • @Autowired позволяет отметить место вставки бина как необязательное @Autowired(required = false); • при замене Spring Framework на другой фреймворк менять аннотацию @Resource не нужно. Расскажите про аннотацию @Inject @Inject входит в пакет javax.inject. Чтобы ее использовать, нужно добавить зависимость: @Inject (аннотация java) – аналог @Autowired (аннотация spring) в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. В ней нет параметров. Поэтому при использовании конкретного имени (Id) бина используется @Named: @Inject @Named("yetAnotherFieldInjectDependency") private ArbitraryDependency yetAnotherFieldInjectDependency; Расскажите про аннотацию @Lookup Обычно бины в приложении Spring являтся синглтонами и для внедрения зависимостей используется конструктор или сеттер. Но бывает и другая ситуация: имеется бин Car – синглтон (singleton bean) – и ему требуется каждый раз новый экземпляр бина Passenger. То есть Car – синглтон, а Passenger – так называемый прототипный бин (prototype bean). Жизненные циклы бинов разные. Бин Car создается контейнером только раз, а бин Passenger создается каждый раз новый. Допустим, это происходит каждый раз при вызове какого-то метода бина Car. Вот здесь и пригодится внедрение бина с помощью метода Lookup. Оно происходит не при инициализации контейнера, а позднее: каждый раз, когда вызывается метод. Суть в том, что создается метод-заглушка в бине Car и он помечается специальным образом – аннотацией @Lookup. Этот метод должен возвращать бин Passenger, каждый раз новый. Контейнер Spring под капотом создаст подкласс и переопределит этот метод и будет выдавать новый экземпляр бина Passenger при каждом вызове аннотированного метода. Даже если в заглушке он возвращает null (а так и надо делать, все равно этот метод будет переопределен). Можно ли вставить бин в статическое поле? Почему? Spring не позволяет внедрять бины напрямую в статические поля. Это связано с тем, что когда загрузчик классов загружает статические значения, контекст Spring еще не загружен. Чтобы исправить это, можно создать нестатический сеттер-метод с @Autowired: private static OrderItemService orderItemService; @Autowired public void setOrderItemService(OrderItemService orderItemService) { TestDataInit.orderItemService = orderItemService; } Расскажите про аннотации @Primary и @Qualifier @Qualifier применяется, если кандидатов для автоматического связывания несколько, аннотация позволяет указать в качестве аргумента имя конкретного бина, который следует внедрить. Она может быть применена к отдельному полю класса, к отдельному аргументу метода или конструктора: public class AutowiredClass { @Autowired //к полям класса @Qualifier("main") private GreetingService greetingService; @Autowired //к отдельному аргументу конструктора или метода public void prepare(@Qualifier("main") GreetingService greetingService){ /* что-то делаем... */ }; } Поэтому у одной из реализации |