diff --git a/README.md b/README.md index 5e75207..ce1294a 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,29 @@ # Вопросы для собеседования на Java Developer + + +# Тут могут быть ошибки/неточности - используйте на свой страх и риск. Но буду благодарен, если укажете на косяки) + + [ООП](#ООП) + [Java Core](#java-core) + [Java Collections Framework](#java-collections) -+ [Java 8](#java-8) ++ [Java 8](#java-8) ++ [Java 9-16](#java-9-16) + [Потоки ввода-вывода в Java](#Потоки-вводавывода-в-java) + [Сериализация](#Сериализация) -+ [Многопоточность](#Многопоточность) -+ [Servlets, JSP, JSTL](#servlets-jsp-jstl) ++ [Многопоточность](#Многопоточность) + [Базы данных](#Базы-данных) + [SQL](#sql) + [JDBC](#jdbc) + [Spring](#spring) ++ [Алгоритмы](#algorithms) ++ [Kotlin](#kotlin) ++ [Шаблоны проектирования](#Шаблоны-проектирования) + [Тестирование](#Тестирование) -+ [Журналирование](#Журналирование) -+ [UML](#uml) ++ [Журналирование](#Журналирование) ++ [Servlets, JSP, JSTL](#servlets-jsp-jstl) ++ [UML](#uml) + [XML](#xml) -+ [Шаблоны проектирования](#Шаблоны-проектирования) + [Основы HTML](#Основы-html) + [Основы CSS](#Основы-css) + [Основы Web](#Основы-web) @@ -36,6 +43,12 @@ + [В чем разница между _композицией_ и _агрегацией_?](oop.md#В-чем-разница-между-композицией-и-агрегацией) + [Что такое _статическое_ и _динамическое связывание_?](oop.md#Что-такое-статическое-и-динамическое-связывание) + [Принцип SOLID?](oop.md#Принцип-SOLID) ++ [Что такое Монолит, Микросервис?](oop.md#Что-такое-Монолит,-Микросервис) ++ [Что выбрать Монолит или Микросервис?](oop.md#Что-выбрать-Монолит-или-Микросервис) ++ [Взаимодействие между микросервисами](oop.md#Взаимодействие-между-микросервисами) ++ [OpenAPI](oop.md#OpenAPI) ++ [Тестирование микросервисов](oop.md#Тестирование-микросервисов) ++ [Распределенная трассировка](oop.md#Распределенная-трассировка) [к оглавлению](#Вопросы-для-собеседования-на-java-developer) @@ -308,6 +321,17 @@ [к оглавлению](#Вопросы-для-собеседования-на-java-developer) +## Java 9-16 ++ [Java 9](java9-16.md#Java-9) ++ [Java 10](java9-16.md#Java-10) ++ [Java 11](java9-16.md#Java-11) ++ [Java 12](java9-16.md#Java-12) ++ [Java 13](java9-16.md#Java-13) ++ [Java 14](java9-16.md#Java-14) ++ [Java 15](java9-16.md#Java-15) ++ [Java 16](java9-16.md#Java-16) +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) + ## Потоки ввода/вывода в Java + [В чём заключается разница между IO и NIO?](io.md#В-чём-заключается-разница-между-io-и-nio) + [Какие особенности NIO вы знаете?](io.md#Какие-особенности-nio-вы-знаете) @@ -430,104 +454,6 @@ [к оглавлению](#Вопросы-для-собеседования-на-java-developer) -## Servlets, JSP, JSTL -+ [Что такое _«сервлет»_?](servlets.md#Что-такое-сервлет) -+ [В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?](servlets.md#В-чем-заключаются-преимущества-технологии-сервлетов-над-cgi-common-gateway-interface) -+ [Какова структура веб-проекта?](servlets.md#Какова-структура-веб-проекта) -+ [Что такое _«контейнер сервлетов»_?](servlets.md#Что-такое-контейнер-сервлетов) -+ [Зачем нужны сервера приложений, если есть контейнеры сервлетов?](servlets.md#Зачем-нужны-сервера-приложений-если-есть-контейнеры-сервлетов) -+ [Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?](servlets.md#Как-контейнер-сервлетов-управляет-жизненным-циклом-сервлета-когда-и-какие-методы-вызываются) -+ [Что такое _«дескриптор развертывания»_?](servlets.md#Что-такое-дескриптор-развертывания) -+ [Какие действия необходимо проделать при создании сервлетов?](servlets.md#Какие-действия-необходимо-проделать-при-создании-сервлетов) -+ [В каком случае требуется переопределять метод `service()`?](servlets.md#В-каком-случае-требуется-переопределять-метод-service) -+ [Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?](servlets.md#Есть-ли-смысл-определять-для-сервлета-конструктор-Каким-образом-лучше-инициализировать-данные) -+ [Почему необходимо переопределить только `init()` метод без аргументов?](servlets.md#Почему-необходимо-переопределить-только-init-метод-без-аргументов) -+ [Какие наиболее распространенные задачи выполняются в контейнере сервлетов?](servlets.md#Какие-наиболее-распространенные-задачи-выполняются-в-контейнере-сервлетов) -+ [Что вы знаете о _сервлетных фильтрах_?](servlets.md#Что-вы-знаете-о-сервлетных-фильтрах) -+ [Зачем в сервлетах используются различные _listener_?](servlets.md#Зачем-в-сервлетах-используются-различные-listener) -+ [Когда стоит использовать фильтры сервлетов, а когда слушателей?](servlets.md#Когда-стоит-использовать-фильтры-сервлетов-а-когда-слушателей) -+ [Как реализовать запуск сервлета одновременно с запуском приложения?](servlets.md#Как-реализовать-запуск-сервлета-одновременно-с-запуском-приложения) -+ [Как обработать в приложении исключения, выброшенные другим сервлетом?](servlets.md#Как-обработать-в-приложении-исключения-выброшенные-другим-сервлетом) -+ [Что представляет собой `ServletConfig`?](servlets.md#Что-представляет-собой-servletconfig) -+ [Что представляет собой `ServletContext`?](servlets.md#Что-представляет-собой-servletcontext) -+ [В чем отличия `ServletContext` и `ServletConfig`?](servlets.md#В-чем-отличия-servletcontext-и-servletconfig) -+ [Для чего нужен интерфейс `ServletResponse`?](servlets.md#Для-чего-нужен-интерфейс-servletresponse) -+ [Для чего нужен интерфейс `ServletRequest`?](servlets.md#Для-чего-нужен-интерфейс-servletrequest) -+ [Что такое `Request Dispatcher`?](servlets.md#Что-такое-request-dispatcher) -+ [Как из одного сервлета вызвать другой сервлет?](servlets.md#Как-из-одного-сервлета-вызвать-другой-сервлет) -+ [Чем отличается `sendRedirect()` от `forward()`?](servlets.md#Чем-отличается-sendredirect-от-forward) -+ [Для чего используются атрибуты сервлетов и как происходит работа с ними?](servlets.md#Для-чего-используются-атрибуты-сервлетов-и-как-происходит-работа-с-ними) -+ [Каким образом можно допустить в сервлете deadlock?](servlets.md#Каким-образом-можно-допустить-в-сервлете-deadlock) -+ [Как получить реальное расположение сервлета на сервере?](servlets.md#Как-получить-реальное-расположение-сервлета-на-сервере) -+ [Как получить информацию о сервере из сервлета?](servlets.md#Как-получить-информацию-о-сервере-из-сервлета) -+ [Как получить IP адрес клиента на сервере?](servlets.md#Как-получить-ip-адрес-клиента-на-сервере) -+ [Какие классы-обертки для сервлетов вы знаете?](servlets.md#Какие-классы-обертки-для-сервлетов-вы-знаете) -+ [В чем отличия `GenericServlet` и `HttpServlet`?](servlets.md#В-чем-отличия-genericservlet-и-httpservlet) -+ [Почему `HttpServlet` класс объявлен как абстрактный?](servlets.md#Почему-httpservlet-класс-объявлен-как-абстрактный) -+ [Какие основные методы присутствуют в классе `HttpServlet`?](servlets.md#Какие-основные-методы-присутствуют-в-классе-httpservlet) -+ [Стоит ли волноваться о многопоточной безопасности работая с сервлетами?](servlets.md#Стоит-ли-волноваться-о-многопоточной-безопасности-работая-с-сервлетами) -+ [Какой метод HTTP не является неизменяемым?](servlets.md#Какой-метод-http-не-является-неизменяемым) -+ [Какие есть методы отправки данных с клиента на сервер?](servlets.md#Какие-есть-методы-отправки-данных-с-клиента-на-сервер) -+ [В чем разница между методами `GET` и `POST`?](servlets.md#В-чем-разница-между-методами-get-и-post) -+ [В чем разница между `PrintWriter` и `ServletOutputStream`?](servlets.md#В-чем-разница-между-printwriter-и-servletoutputstream) -+ [Можно ли одновременно использовать в сервлете `PrintWriter` и `ServletOutputStream`?](servlets.md#Можно-ли-одновременно-использовать-в-сервлете-printwriter-и-servletoutputstream) -+ [Расскажите об интерфейсе `SingleThreadModel`.](servlets.md#Расскажите-об-интерфейсе-singlethreadmodel) -+ [Что означает _URL encoding_? Как это осуществить в Java?](servlets.md#Что-означает-url-encoding-Как-это-осуществить-в-java) -+ [Какие различные методы управления сессией в сервлетах вы знаете?](servlets.md#Какие-различные-методы-управления-сессией-в-сервлетах-вы-знаете) -+ [Что такое _cookies_?](servlets.md#Что-такое-cookies) -+ [Какие методы для работы с cookies предусмотрены в сервлетах?](servlets.md#Какие-методы-для-работы-с-cookies-предусмотрены-в-сервлетах) -+ [Что такое _URL Rewriting_?](servlets.md#Что-такое-url-rewriting) -+ [Зачем нужны и чем отличаются методы `encodeURL()` и `encodeRedirectURL()`?](servlets.md#Зачем-нужны-и-чем-отличаются-методы-encodeurl-и-encoderedirecturl) -+ [Что такое _«сессия»_?](servlets.md#Что-такое-сессия) -+ [Как уведомить объект в сессии, что сессия недействительна или закончилась?](servlets.md#Как-уведомить-объект-в-сессии-что-сессия-недействительна-или-закончилась) -+ [Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?](servlets.md#Какой-существует-эффективный-способ-удостоверится-что-все-сервлеты-доступны-только-для-пользователя-с-верной-сессией) -+ [Как мы можем обеспечить _transport layer security_ для нашего веб приложения?](servlets.md#Как-мы-можем-обеспечить-transport-layer-security-для-нашего-веб-приложения) -+ [Как организовать подключение к базе данных, обеспечить журналирование в сервлете?](servlets.md#Как-организовать-подключение-к-базе-данных-обеспечить-журналирование-в-сервлете) -+ [Какие основные особенности появились в спецификации _Servlet 3_?](servlets.md#Какие-основные-особенности-появились-в-спецификации-servlet-3) -+ [Какие способы аутентификации доступны сервлету?](servlets.md#Какие-способы-аутентификации-доступны-сервлету) -+ [Что такое _Java Server Pages (JSP)_?](servlets.md#Что-такое-java-server-pages-jsp) -+ [Зачем нужен JSP?](servlets.md#Зачем-нужен-jsp) -+ [Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.](servlets.md#Опишите-как-обрабатываются-jsp-страницы-начиная-от-запроса-к-серверу-заканчивая-ответом-пользователю) -+ [Расскажите об этапах (фазах) жизненного цикла JSP.](servlets.md#Расскажите-об-этапах-фазах-жизненного-цикла-jsp) -+ [Расскажите о методах жизненного цикла JSP.](servlets.md#Расскажите-о-методах-жизненного-цикла-jsp) -+ [Какие методы жизненного цикла JSP могут быть переопределены?](servlets.md#Какие-методы-жизненного-цикла-jsp-могут-быть-переопределены) -+ [Как можно предотвратить прямой доступ к JSP странице из браузера?](servlets.md#Как-можно-предотвратить-прямой-доступ-к-jsp-странице-из-браузера) -+ [Какая разница между _динамическим_ и _статическим_ содержимым JSP?](servlets.md#Какая-разница-между-динамическим-и-статическим-содержимым-jsp) -+ [Как закомментировать код в JSP?](servlets.md#Как-закомментировать-код-в-jsp) -+ [Какие существуют основные типы тегов JSP?](servlets.md#Какие-существуют-основные-типы-тегов-jsp) -+ [Что вы знаете о действиях JSP (_Action tag_ и _JSP Action Elements_).](servlets.md#Что-вы-знаете-о-действиях-jsp-action-tag-и-jsp-action-elements) -+ [Взаимодействие _JSP - сервлет - JSP_.](servlets.md#Взаимодействие-jsp---сервлет---jsp) -+ [Какие области видимости переменных существуют в JSP?](servlets.md#Какие-области-видимости-переменных-существуют-в-jsp) -+ [Какие неявные, внутренние объекты и методы есть на JSP странице?](servlets.md#Какие-неявные-внутренние-объекты-и-методы-есть-на-jsp-странице) -+ [Какие неявные объекты не доступны в обычной JSP странице?](servlets.md#Какие-неявные-объекты-не-доступны-в-обычной-jsp-странице) -+ [Что вы знаете о `PageContext` и какие преимущества его использования?](servlets.md#Что-вы-знаете-о-pagecontext-и-какие-преимущества-его-использования) -+ [Как сконфигурировать параметры инициализации для JSP?](servlets.md#Как-сконфигурировать-параметры-инициализации-для-jsp) -+ [Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?](servlets.md#Почему-не-рекомендуется-использовать-скриплеты-скриптовые-элементы-в-jsp) -+ [Можно ли определить класс внутри JSP страницы?](servlets.md#Можно-ли-определить-класс-внутри-jsp-страницы) -+ [Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?](servlets.md#Что-вы-знаете-о-Языке-выражений-jsp-jsp-expression-language--el) -+ [Какие типы EL операторов вы знаете?](servlets.md#Какие-типы-el-операторов-вы-знаете) -+ [Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.](servlets.md#Назовите-неявные-внутренние-объекты-jsp-el-и-их-отличия-от-объектов-jsp) -+ [Как отключить возможность использования EL в JSP?](servlets.md#Как-отключить-возможность-использования-el-в-jsp) -+ [Как узнать тип HTTP метода используя JSP EL?](servlets.md#Как-узнать-тип-http-метода-используя-jsp-el) -+ [Что такое _JSTL (JSP Standard tag library)_?](servlets.md#Что-такое-jstl-jsp-standard-tag-library) -+ [Из каких групп тегов состоит библиотека _JSTL_?](servlets.md#Из-каких-групп-тегов-состоит-библиотека-jstl) -+ [Какая разница между `` и ``?](servlets.md#Какая-разница-между-cset-и-jspusebean) -+ [Чем отличается `` от `` и директивы `<%@include %>`?](servlets.md#Чем-отличается-cimport-от-jspinclude-и-директивы-include-) -+ [Как можно расширить функциональность JSP?](servlets.md#Как-можно-расширить-функциональность-jsp) -+ [Что вы знаете о написании пользовательских JSP тегов?](servlets.md#Что-вы-знаете-о-написании-пользовательских-jsp-тегов) -+ [Приведите пример использования собственных тегов.](servlets.md#Приведите-пример-использования-собственных-тегов) -+ [Как сделать перенос строки в HTML средствами JSP?](servlets.md#Как-сделать-перенос-строки-в-html-средствами-jsp) -+ [Почему не нужно конфигурировать стандартные JSP теги в `web.xml`?](servlets.md#Почему-не-нужно-конфигурировать-стандартные-jsp-теги-в-webxml) -+ [Как можно обработать ошибки JSP страниц?](servlets.md#Как-можно-обработать-ошибки-jsp-страниц) -+ [Как происходит обработка ошибок с помощью JSTL?](servlets.md#Как-происходит-обработка-ошибок-с-помощью-jstl) -+ [Как конфигурируется JSP в дескрипторе развертывания.](servlets.md#Как-конфигурируется-jsp-в-дескрипторе-развертывания) -+ [Можно ли использовать Javascript на JSP странице?](servlets.md#Можно-ли-использовать-javascript-на-jsp-странице) -+ [Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?](servlets.md#Всегда-ли-создается-объект-сессии-на-jsp-странице-можно-ли-отключить-его-создание) -+ [Какая разница между `JSPWriter` и сервлетным `PrintWriter`?](servlets.md#Какая-разница-между-jspwriter-и-сервлетным-printwriter) -+ [Опишите общие практические принципы работы с JSP.](servlets.md#Опишите-общие-практические-принципы-работы-с-jsp) - -[к оглавлению](#Вопросы-для-собеседования-на-java-developer) - ## Базы данных + [Что такое _«база данных»_?](db.md#Что-такое-база-данных) + [Что такое _«система управления базами данных»_?](db.md#Что-такое-система-управления-базами-данных) @@ -591,20 +517,68 @@ [к оглавлению](#Вопросы-для-собеседования-на-java-developer) ## JDBC -+ [Что такое _JDBC_?](jdbc.md#Что-такое-jdbc) ++ [Что такое ORM?](jdbc.md#Что-такое-orm) ++ [Что такое JDBC?](jdbc.md#Что-такое-jdbc) ++ [Что такое JPA?](jdbc.md#Что-такое-jpa) ++ [Различия между JPA JDBC и Hibernate?](jdbc.md#Различия-между-jpa-jdbc-и-hibernate) ++ [Что такое JPQL и HQL?](jdbc.md#Что-такое-jpql-и-hql) + [В чем заключаются преимущества использования JDBC?](jdbc.md#В-чем-заключаются-преимущества-использования-jdbc) ++ [В чем заключаются преимущества использования Hibernate?](jdbc.md#В-чем-заключаются-преимущества-использования-hibernate) + [Что из себя представляет JDBC URL?](jdbc.md#Что-из-себя-представляет-jdbc-url) + [Из каких частей стоит JDBC?](jdbc.md#Из-каких-частей-стоит-jdbc) + [Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?](jdbc.md#Перечислите-основные-классы-и-интерфейсы-jdbc) + [Опишите основные этапы работы с базой данных с использованием JDBC.](jdbc.md#Опишите-основные-этапы-работы-с-базой-данных-при-использовании-jdbc) + [Как зарегистрировать драйвер JDBC?](jdbc.md#Как-зарегистрировать-драйвер-jdbc) + [Как установить соединение с базой данных?](jdbc.md#Как-установить-соединение-с-базой-данных) ++ [Транзакции в Hibernate.](jdbc.md#Транзакции-в-Hibernate) + [Какие уровни изоляции транзакций поддерживаются в JDBC?](jdbc.md#Какие-уровни-изоляции-транзакций-поддерживаются-в-jdbc) + [При помощи чего формируются запросы к базе данных?](jdbc.md#При-помощи-чего-формируются-запросы-к-базе-данных) + [Чем отличается Statement от PreparedStatement?](jdbc.md#Чем-отличается-statement-от-preparedstatement) + [Как осуществляется запрос к базе данных и обработка результатов?](jdbc.md#Как-осуществляется-запрос-к-базе-данных-и-обработка-результатов) + [Как вызвать хранимую процедуру?](jdbc.md#Как-вызвать-хранимую-процедуру) + [Как закрыть соединение с базой данных?](jdbc.md#Как-закрыть-соединение-с-базой-данных) ++ [Что такое Entity?](jdbc.md#Что-такое-entity) ++ [Как наследуется Entity?](jdbc.md#Как-наследуется-entity) ++ [Что такое POJO класс?](jdbc.md#Что-такое-pojo-класс) ++ [Какие типы данных можно использовать в атрибутах, входящих в первичный ключ Entity класса (составной или простой), чтобы полученный первичный ключ мог использоваться для любой базы данных](jdbc.md#Какие-типы-данных-можно-использовать-в-атрибутах-входящих-в-первичный-ключ-entity-класса-составной-или-простой-чтобы-полученный-первичный-ключ-мог-использоваться-для-любой-базы-данных) ++ [Что такое встраиваемый (Embeddable) класс?](jdbc.md#Что-такое-встраиваемый-(embeddable)-класс) ++ [Может ли встраиваемый (Embeddable) класс содержать другой встраиваемый (Embeddable) класс?](jdbc.md#Может-ли-встраиваемый-(embeddable)-класс-содержать-другой-встраиваемый-(embeddable)-класс) ++ [Какие требования JPA устанавливает к встраиваемым (Embeddable) классам?](jdbc.md#Какие-требования-jpa-устанавливает-к-встраиваемым-(embeddable)-классам) ++ [Основные классы и интерфейсы JPA?](jdbc.md#Основные-классы-и-интерфейсы-jpa) ++ [Что такое Метамодель?](jdbc.md#Что-такое-метамодель) ++ [Основные классы и интерфейсы Hibernate?](jdbc.md#Основные-классы-и-интерфейсы-hibernate) ++ [Способы сконфигурировать Hibernate?](jdbc.md#Способы-сконфигурировать-hibernate) ++ [SessionFactory vs EntityManagerFactory и Session vs EntityManager?](jdbc.md#sessionfactory-vs-entitymanagerfactory-и-session-vs-entitymanager?) ++ [Жизненный цикл Entity?](jdbc.md#Жизненный-цикл-entity) ++ [Влияние операций EntityManager на Entity объекты различный жизненных циклов?](jdbc.md#Влияние-операций-entitymanager-на-entity-объекты-различный-жизненных-циклов) ++ [Аннотации JPA?](jdbc.md#Аннотации-jpa) ++ [Аннотации Hibernate?](jdbc.md#Аннотации-hibernate) ++ [Кеширование в Hibernate?](jdbc.md#Кеширование-в-hibernate) ++ [Стратегии кеширования?](jdbc.md#Стратегии-кеширования) ++ [Что такое Mapped Superclass?](jdbc.md#Что-такое-mapped-superclass) ++ [Какие три типа стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA?](jdbc.md#Какие-три-типа-стратегии-наследования-мапинга-(inheritance-mapping-strategies)-описаны-в-jpa) ++ [Стратегии загрузки объектов в Hibernate?](jdbc.md#Стратегии-загрузки-объектов-в-hibernat) ++ [Для чего нужна аннотация Basic?](jdbc.md#Для-чего-нужна-аннотация-basic) ++ [Для чего нужна аннотация Access?](jdbc.md#Для-чего-нужна-аннотация-access) ++ [Для чего нужны callback методы в JPA? К каким сущностям применяются аннотации callback методов? Перечислите семь callback методов (или, что тоже самое, аннотаций callback методов)](jdbc.md#Для-чего-нужны-callback-методы-в-jpa) ++ [Какие видов блокировок (lock) описаны в спецификации JPA?](jdbc.md#Какие-видов-блокировок-(lock)-описаны-в-спецификации-jpa) ++ [Как можно изменить настройки fetch?](jdbc.md#Как-можно-изменить-настройки-fetch) ++ [Что означает полиморфизм (polymorphism) в запросах JPQL (Java Persistence query language) и как его «выключить»?](jdbc.md#Что-означает-полиморфизм-(polymorphism)-в-запросах-jpql-(java-persistence-query-language)-и-как-его-«выключить») ++ [Каскадирование](jdbc.md#Каскадирование) ++ [Как определить владельца связи?](jdbc.md#Как-определить-владельца-связи) ++ [Что происходит с таблицами при изпользовании mappedBy?](jdbc.md#Что-происходит-с-таблицами-при-изпользовании-mappedby) ++ [FetchType стратегии по-умолчанию?](jdbc.md#fetchtype-стратегии-по-умолчанию) ++ [Data и Enum значения в БД?](jdbc.md#data-и-enum-значения-в-БД) ++ [PersistenceContext](jdbc.md#persistencecontext) ++ [Entity graph](jdbc.md#Entity-graph) ++ [Пробелма n+1 select и как ее решить?](jdbc.md#Пробелма-n+1-select-и-как-ее-решить) ++ [JOIN FETCH vs Join](jdbc.md#join-fetch-vs-join) ++ [Criteria Api](jdbc.md#Criteria-Api) ++ [OrderBy vs OrderColumn](jdbc.md#orderby-vs-ordercolumn) ++ [Как создать составной ключ](jdbc.md#Как-создать-составной-ключ) ++ [GeneratedValue](jdbc.md#generatedvalue) ++ [OrphanRemoval vs cascadeType.remove](jdbc.md#Orphanremoval-vs-cascadeType.remove) ++ [ElementCollection](jdbc.md#Elementcollection) [к оглавлению](#Вопросы-для-собеседования-на-java-developer) @@ -653,7 +627,26 @@ + [Что нового в Spring 5](spring.md#Что-нового-в-spring-5) + [RestTemplate и JDBCTemplate](spring.md#RestTemplate-и-JDBCTemplate) + [Socket](spring.md#Socket) -[к оглавлению](spring.md#Вопросы-для-собеседования-на-java-developer) +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) + +## Алгоритмы +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) + +## Kotlin +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) + +## Шаблоны проектирования ++ [Что такое _«шаблон проектирования»_?](patterns.md#Что-такое-шаблон-проектирования) ++ [Назовите основные характеристики шаблонов.](patterns.md#Назовите-основные-характеристики-шаблонов) ++ [Типы шаблонов проектирования.](patterns.md#Типы-шаблонов-проектирования) ++ [Приведите примеры основных шаблонов проектирования.](patterns.md#Приведите-примеры-основных-шаблонов-проектирования) ++ [Приведите примеры порождающих шаблонов проектирования.](patterns.md#Приведите-примеры-порождающих-шаблонов-проектирования) ++ [Приведите примеры структурных шаблонов проектирования.](patterns.md#Приведите-примеры-структурных-шаблонов-проектирования) ++ [Приведите примеры поведенческих шаблонов проектирования.](patterns.md#Приведите-примеры-поведенческих-шаблонов-проектирования) ++ [Что такое _«антипаттерн»_? Какие антипаттерны вы знаете?](patterns.md#Что-такое-антипаттерн-Какие-антипаттерны-вы-знаете) ++ [Что такое _Dependency Injection_?](patterns.md#Что-такое-dependency-injection) + +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) ## Тестирование + [Что такое _«модульное тестирование»_?](test.md#Что-такое-модульное-тестирование) @@ -678,6 +671,104 @@ [к оглавлению](#Вопросы-для-собеседования-на-java-developer) +## Servlets, JSP, JSTL ++ [Что такое _«сервлет»_?](servlets.md#Что-такое-сервлет) ++ [В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?](servlets.md#В-чем-заключаются-преимущества-технологии-сервлетов-над-cgi-common-gateway-interface) ++ [Какова структура веб-проекта?](servlets.md#Какова-структура-веб-проекта) ++ [Что такое _«контейнер сервлетов»_?](servlets.md#Что-такое-контейнер-сервлетов) ++ [Зачем нужны сервера приложений, если есть контейнеры сервлетов?](servlets.md#Зачем-нужны-сервера-приложений-если-есть-контейнеры-сервлетов) ++ [Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?](servlets.md#Как-контейнер-сервлетов-управляет-жизненным-циклом-сервлета-когда-и-какие-методы-вызываются) ++ [Что такое _«дескриптор развертывания»_?](servlets.md#Что-такое-дескриптор-развертывания) ++ [Какие действия необходимо проделать при создании сервлетов?](servlets.md#Какие-действия-необходимо-проделать-при-создании-сервлетов) ++ [В каком случае требуется переопределять метод `service()`?](servlets.md#В-каком-случае-требуется-переопределять-метод-service) ++ [Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?](servlets.md#Есть-ли-смысл-определять-для-сервлета-конструктор-Каким-образом-лучше-инициализировать-данные) ++ [Почему необходимо переопределить только `init()` метод без аргументов?](servlets.md#Почему-необходимо-переопределить-только-init-метод-без-аргументов) ++ [Какие наиболее распространенные задачи выполняются в контейнере сервлетов?](servlets.md#Какие-наиболее-распространенные-задачи-выполняются-в-контейнере-сервлетов) ++ [Что вы знаете о _сервлетных фильтрах_?](servlets.md#Что-вы-знаете-о-сервлетных-фильтрах) ++ [Зачем в сервлетах используются различные _listener_?](servlets.md#Зачем-в-сервлетах-используются-различные-listener) ++ [Когда стоит использовать фильтры сервлетов, а когда слушателей?](servlets.md#Когда-стоит-использовать-фильтры-сервлетов-а-когда-слушателей) ++ [Как реализовать запуск сервлета одновременно с запуском приложения?](servlets.md#Как-реализовать-запуск-сервлета-одновременно-с-запуском-приложения) ++ [Как обработать в приложении исключения, выброшенные другим сервлетом?](servlets.md#Как-обработать-в-приложении-исключения-выброшенные-другим-сервлетом) ++ [Что представляет собой `ServletConfig`?](servlets.md#Что-представляет-собой-servletconfig) ++ [Что представляет собой `ServletContext`?](servlets.md#Что-представляет-собой-servletcontext) ++ [В чем отличия `ServletContext` и `ServletConfig`?](servlets.md#В-чем-отличия-servletcontext-и-servletconfig) ++ [Для чего нужен интерфейс `ServletResponse`?](servlets.md#Для-чего-нужен-интерфейс-servletresponse) ++ [Для чего нужен интерфейс `ServletRequest`?](servlets.md#Для-чего-нужен-интерфейс-servletrequest) ++ [Что такое `Request Dispatcher`?](servlets.md#Что-такое-request-dispatcher) ++ [Как из одного сервлета вызвать другой сервлет?](servlets.md#Как-из-одного-сервлета-вызвать-другой-сервлет) ++ [Чем отличается `sendRedirect()` от `forward()`?](servlets.md#Чем-отличается-sendredirect-от-forward) ++ [Для чего используются атрибуты сервлетов и как происходит работа с ними?](servlets.md#Для-чего-используются-атрибуты-сервлетов-и-как-происходит-работа-с-ними) ++ [Каким образом можно допустить в сервлете deadlock?](servlets.md#Каким-образом-можно-допустить-в-сервлете-deadlock) ++ [Как получить реальное расположение сервлета на сервере?](servlets.md#Как-получить-реальное-расположение-сервлета-на-сервере) ++ [Как получить информацию о сервере из сервлета?](servlets.md#Как-получить-информацию-о-сервере-из-сервлета) ++ [Как получить IP адрес клиента на сервере?](servlets.md#Как-получить-ip-адрес-клиента-на-сервере) ++ [Какие классы-обертки для сервлетов вы знаете?](servlets.md#Какие-классы-обертки-для-сервлетов-вы-знаете) ++ [В чем отличия `GenericServlet` и `HttpServlet`?](servlets.md#В-чем-отличия-genericservlet-и-httpservlet) ++ [Почему `HttpServlet` класс объявлен как абстрактный?](servlets.md#Почему-httpservlet-класс-объявлен-как-абстрактный) ++ [Какие основные методы присутствуют в классе `HttpServlet`?](servlets.md#Какие-основные-методы-присутствуют-в-классе-httpservlet) ++ [Стоит ли волноваться о многопоточной безопасности работая с сервлетами?](servlets.md#Стоит-ли-волноваться-о-многопоточной-безопасности-работая-с-сервлетами) ++ [Какой метод HTTP не является неизменяемым?](servlets.md#Какой-метод-http-не-является-неизменяемым) ++ [Какие есть методы отправки данных с клиента на сервер?](servlets.md#Какие-есть-методы-отправки-данных-с-клиента-на-сервер) ++ [В чем разница между методами `GET` и `POST`?](servlets.md#В-чем-разница-между-методами-get-и-post) ++ [В чем разница между `PrintWriter` и `ServletOutputStream`?](servlets.md#В-чем-разница-между-printwriter-и-servletoutputstream) ++ [Можно ли одновременно использовать в сервлете `PrintWriter` и `ServletOutputStream`?](servlets.md#Можно-ли-одновременно-использовать-в-сервлете-printwriter-и-servletoutputstream) ++ [Расскажите об интерфейсе `SingleThreadModel`.](servlets.md#Расскажите-об-интерфейсе-singlethreadmodel) ++ [Что означает _URL encoding_? Как это осуществить в Java?](servlets.md#Что-означает-url-encoding-Как-это-осуществить-в-java) ++ [Какие различные методы управления сессией в сервлетах вы знаете?](servlets.md#Какие-различные-методы-управления-сессией-в-сервлетах-вы-знаете) ++ [Что такое _cookies_?](servlets.md#Что-такое-cookies) ++ [Какие методы для работы с cookies предусмотрены в сервлетах?](servlets.md#Какие-методы-для-работы-с-cookies-предусмотрены-в-сервлетах) ++ [Что такое _URL Rewriting_?](servlets.md#Что-такое-url-rewriting) ++ [Зачем нужны и чем отличаются методы `encodeURL()` и `encodeRedirectURL()`?](servlets.md#Зачем-нужны-и-чем-отличаются-методы-encodeurl-и-encoderedirecturl) ++ [Что такое _«сессия»_?](servlets.md#Что-такое-сессия) ++ [Как уведомить объект в сессии, что сессия недействительна или закончилась?](servlets.md#Как-уведомить-объект-в-сессии-что-сессия-недействительна-или-закончилась) ++ [Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?](servlets.md#Какой-существует-эффективный-способ-удостоверится-что-все-сервлеты-доступны-только-для-пользователя-с-верной-сессией) ++ [Как мы можем обеспечить _transport layer security_ для нашего веб приложения?](servlets.md#Как-мы-можем-обеспечить-transport-layer-security-для-нашего-веб-приложения) ++ [Как организовать подключение к базе данных, обеспечить журналирование в сервлете?](servlets.md#Как-организовать-подключение-к-базе-данных-обеспечить-журналирование-в-сервлете) ++ [Какие основные особенности появились в спецификации _Servlet 3_?](servlets.md#Какие-основные-особенности-появились-в-спецификации-servlet-3) ++ [Какие способы аутентификации доступны сервлету?](servlets.md#Какие-способы-аутентификации-доступны-сервлету) ++ [Что такое _Java Server Pages (JSP)_?](servlets.md#Что-такое-java-server-pages-jsp) ++ [Зачем нужен JSP?](servlets.md#Зачем-нужен-jsp) ++ [Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.](servlets.md#Опишите-как-обрабатываются-jsp-страницы-начиная-от-запроса-к-серверу-заканчивая-ответом-пользователю) ++ [Расскажите об этапах (фазах) жизненного цикла JSP.](servlets.md#Расскажите-об-этапах-фазах-жизненного-цикла-jsp) ++ [Расскажите о методах жизненного цикла JSP.](servlets.md#Расскажите-о-методах-жизненного-цикла-jsp) ++ [Какие методы жизненного цикла JSP могут быть переопределены?](servlets.md#Какие-методы-жизненного-цикла-jsp-могут-быть-переопределены) ++ [Как можно предотвратить прямой доступ к JSP странице из браузера?](servlets.md#Как-можно-предотвратить-прямой-доступ-к-jsp-странице-из-браузера) ++ [Какая разница между _динамическим_ и _статическим_ содержимым JSP?](servlets.md#Какая-разница-между-динамическим-и-статическим-содержимым-jsp) ++ [Как закомментировать код в JSP?](servlets.md#Как-закомментировать-код-в-jsp) ++ [Какие существуют основные типы тегов JSP?](servlets.md#Какие-существуют-основные-типы-тегов-jsp) ++ [Что вы знаете о действиях JSP (_Action tag_ и _JSP Action Elements_).](servlets.md#Что-вы-знаете-о-действиях-jsp-action-tag-и-jsp-action-elements) ++ [Взаимодействие _JSP - сервлет - JSP_.](servlets.md#Взаимодействие-jsp---сервлет---jsp) ++ [Какие области видимости переменных существуют в JSP?](servlets.md#Какие-области-видимости-переменных-существуют-в-jsp) ++ [Какие неявные, внутренние объекты и методы есть на JSP странице?](servlets.md#Какие-неявные-внутренние-объекты-и-методы-есть-на-jsp-странице) ++ [Какие неявные объекты не доступны в обычной JSP странице?](servlets.md#Какие-неявные-объекты-не-доступны-в-обычной-jsp-странице) ++ [Что вы знаете о `PageContext` и какие преимущества его использования?](servlets.md#Что-вы-знаете-о-pagecontext-и-какие-преимущества-его-использования) ++ [Как сконфигурировать параметры инициализации для JSP?](servlets.md#Как-сконфигурировать-параметры-инициализации-для-jsp) ++ [Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?](servlets.md#Почему-не-рекомендуется-использовать-скриплеты-скриптовые-элементы-в-jsp) ++ [Можно ли определить класс внутри JSP страницы?](servlets.md#Можно-ли-определить-класс-внутри-jsp-страницы) ++ [Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?](servlets.md#Что-вы-знаете-о-Языке-выражений-jsp-jsp-expression-language--el) ++ [Какие типы EL операторов вы знаете?](servlets.md#Какие-типы-el-операторов-вы-знаете) ++ [Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.](servlets.md#Назовите-неявные-внутренние-объекты-jsp-el-и-их-отличия-от-объектов-jsp) ++ [Как отключить возможность использования EL в JSP?](servlets.md#Как-отключить-возможность-использования-el-в-jsp) ++ [Как узнать тип HTTP метода используя JSP EL?](servlets.md#Как-узнать-тип-http-метода-используя-jsp-el) ++ [Что такое _JSTL (JSP Standard tag library)_?](servlets.md#Что-такое-jstl-jsp-standard-tag-library) ++ [Из каких групп тегов состоит библиотека _JSTL_?](servlets.md#Из-каких-групп-тегов-состоит-библиотека-jstl) ++ [Какая разница между `` и ``?](servlets.md#Какая-разница-между-cset-и-jspusebean) ++ [Чем отличается `` от `` и директивы `<%@include %>`?](servlets.md#Чем-отличается-cimport-от-jspinclude-и-директивы-include-) ++ [Как можно расширить функциональность JSP?](servlets.md#Как-можно-расширить-функциональность-jsp) ++ [Что вы знаете о написании пользовательских JSP тегов?](servlets.md#Что-вы-знаете-о-написании-пользовательских-jsp-тегов) ++ [Приведите пример использования собственных тегов.](servlets.md#Приведите-пример-использования-собственных-тегов) ++ [Как сделать перенос строки в HTML средствами JSP?](servlets.md#Как-сделать-перенос-строки-в-html-средствами-jsp) ++ [Почему не нужно конфигурировать стандартные JSP теги в `web.xml`?](servlets.md#Почему-не-нужно-конфигурировать-стандартные-jsp-теги-в-webxml) ++ [Как можно обработать ошибки JSP страниц?](servlets.md#Как-можно-обработать-ошибки-jsp-страниц) ++ [Как происходит обработка ошибок с помощью JSTL?](servlets.md#Как-происходит-обработка-ошибок-с-помощью-jstl) ++ [Как конфигурируется JSP в дескрипторе развертывания.](servlets.md#Как-конфигурируется-jsp-в-дескрипторе-развертывания) ++ [Можно ли использовать Javascript на JSP странице?](servlets.md#Можно-ли-использовать-javascript-на-jsp-странице) ++ [Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?](servlets.md#Всегда-ли-создается-объект-сессии-на-jsp-странице-можно-ли-отключить-его-создание) ++ [Какая разница между `JSPWriter` и сервлетным `PrintWriter`?](servlets.md#Какая-разница-между-jspwriter-и-сервлетным-printwriter) ++ [Опишите общие практические принципы работы с JSP.](servlets.md#Опишите-общие-практические-принципы-работы-с-jsp) + +[к оглавлению](#Вопросы-для-собеседования-на-java-developer) + ## UML + [Что такое _UML_?](uml.md#Что-такое-uml) + [Что такое _«диаграмма»_, _«нотация»_ и _«метамодель»_ в UML?](uml.md#Что-такое-диаграмма-нотация-и-метамодель-в-uml) @@ -701,19 +792,6 @@ [к оглавлению](#Вопросы-для-собеседования-на-java-developer) -## Шаблоны проектирования -+ [Что такое _«шаблон проектирования»_?](patterns.md#Что-такое-шаблон-проектирования) -+ [Назовите основные характеристики шаблонов.](patterns.md#Назовите-основные-характеристики-шаблонов) -+ [Типы шаблонов проектирования.](patterns.md#Типы-шаблонов-проектирования) -+ [Приведите примеры основных шаблонов проектирования.](patterns.md#Приведите-примеры-основных-шаблонов-проектирования) -+ [Приведите примеры порождающих шаблонов проектирования.](patterns.md#Приведите-примеры-порождающих-шаблонов-проектирования) -+ [Приведите примеры структурных шаблонов проектирования.](patterns.md#Приведите-примеры-структурных-шаблонов-проектирования) -+ [Приведите примеры поведенческих шаблонов проектирования.](patterns.md#Приведите-примеры-поведенческих-шаблонов-проектирования) -+ [Что такое _«антипаттерн»_? Какие антипаттерны вы знаете?](patterns.md#Что-такое-антипаттерн-Какие-антипаттерны-вы-знаете) -+ [Что такое _Dependency Injection_?](patterns.md#Что-такое-dependency-injection) - -[к оглавлению](#Вопросы-для-собеседования-на-java-developer) - ## Основы HTML + [Что такое _«HTML»_?](html.md#Что-такое-html) + [Что такое _«XHTML»_?](html.md#Что-такое-xhtml) diff --git "a/\320\220\320\273\320\263\320\276\321\200\320\270\321\202\320\274\321\213.md" b/algorithms.md similarity index 100% rename from "\320\220\320\273\320\263\320\276\321\200\320\270\321\202\320\274\321\213.md" rename to algorithms.md diff --git a/concurrency.md b/concurrency.md index bc78c49..3b3790b 100644 --- a/concurrency.md +++ b/concurrency.md @@ -43,6 +43,7 @@ + [Что значит _«усыпить»_ поток?](#Что-значит-усыпить-поток) + [Чем отличаются два интерфейса `Runnable` и `Callable`?](#Чем-отличаются-два-интерфейса-runnable-и-callable) + [Что такое `FutureTask`?](#Что-такое-futuretask) ++ [java.util.concurrent](#java.util.concurrent) + [В чем заключаются различия между `CyclicBarrier` и `CountDownLatch`?](#В-чем-заключаются-различия-между-cyclicbarrier-и-countdownlatch) + [Что такое _race condition_?](#Что-такое-race-condition) + [Существует ли способ решения проблемы _race condition_?](#Существует-ли-способ-решения-проблемы-race-condition) @@ -81,11 +82,13 @@ ## Расскажите о модели памяти Java? __Модель памяти Java (Java Memory Model, JMM)__ описывает поведение потоков в среде исполнения Java. Это часть семантики языка Java, набор правил, описывающий выполнение многопоточных программ и правил, по которым потоки могут взаимодействовать друг с другом посредством основной памяти. +Program order - правило порядка выполнения программы. Program order гарантирует, что в отдельных потоках оптимизация переупорядочения, введенная компилятором, не может привести к результатам, отличным от того, что произошло бы, если бы программа выполнялась последовательно. + Формально модель памяти определяет набор действий межпоточного взаимодействия (эти действия включают в себя, в частности, чтение и запись переменной, захват и освобождений монитора, чтение и запись volatile переменной, запуск нового потока), а также модель памяти определяет отношение между этими действиями -_happens-before_ - абстракции обозначающей, что если операция _A_ связана отношением happens-before с операцией _B_, то весь код следуемый за операцией _A_, выполняемый в одном потоке, видит все изменения, сделанные другим потоком, до операции _B_. Т.е. при исполнении команд по порядку, вторая команда видит все изменения, свершенные первой операцией Существует несколько основных правил для отношения happens-before: -+ В рамках одного потока любая операция happens-before любой операцией следующей за ней в исходном коде; ++ В рамках одного потока любая операция идущая в program order является - happens-before по отношению к любой операциией следующей за ней в исходном коде; + Освобождение монитора (unlock) happens-before захват того же монитора (lock); + Выход из `synchronized` блока/метода happens-before вход в `synchronized` блок/метод на том же мониторе; + Запись `volatile` поля happens-before чтение того же самого `volatile` поля; @@ -130,7 +133,7 @@ _Reordering (переупорядочивание)_. Для увеличения Признаки: + Наличие нескольких потоков управления (например _Thread_ в Java, _корутина_ в Kotlin), если поток управления один, то конкурентного выполнения быть не может -+ Недетерминированный результат выполнения. Результат зависит от случайных событий, реализации и того как была проведена синхронизация. Даже если каждый поток полностью детерминированный, итоговый результат будет недетерминированным ++ Недетерминированный (_Независимый друг от друга_) результат выполнения. Результат зависит от случайных событий, реализации и того как была проведена синхронизация. Даже если каждый поток полностью детерминированный, итоговый результат будет недетерминированным Параллелизм — это способ выполнения разных частей одной задачи. @@ -207,6 +210,19 @@ __Зелёные (легковесные) потоки__(green threads) - пот ## Каким образом можно создать поток? + Создать потомка класса `Thread` и переопределить его метод `run()`; + Создать объект класса `Thread`, передав ему в конструкторе экземпляр класса, реализующего интерфейс `Runnable`. Эти интерфейс содержит метод `run()`, который будет выполняться в новом потоке. Поток закончит выполнение, когда завершится его метод `run()`. + +```java +new Thread(new Runnable() { + @Override + public void run() { + someService.insertInBD(name); + } +}).start(); + +//то же самое с лямбдой +new Thread(() -> someService.insertInBD(name)).start(); +``` + + Вызвать метод `submit()` у экземпляра класса реализующего интерфейс `ExecutorService`, передав ему в качестве параметра экземпляр класса реализующего интерфейс `Runnable` или `Callable` (содержит метод `call()`, в котором описывается логика выполнения). [к оглавлению](#Многопоточность) @@ -265,6 +281,7 @@ __Монитор__ – это средство обеспечения контр ## В каких состояниях может находиться поток? Потоки могут находиться в одном из следующих состояний: +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Concarrency1.jpg) + __Новый (New)__. После создания экземпляра потока, он находится в состоянии Новый до тех пор, пока не вызван метод `start()`. В этом состоянии поток не считается живым. + __Работоспособный (Runnable)__. Поток переходит в состояние Работоспособный, когда вызывается метод `start()`. Поток может перейти в это состояние также из состояния Работающий или из состояния Блокирован. Когда поток находится в этом состоянии, он считается живым. @@ -466,6 +483,200 @@ __`synchronized`__ - это зарезервированное слово поз [к оглавлению](#Многопоточность) +## java.util.concurrent + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Concarrency2.png) + ++ Concurrent Collections — набор коллекций, более эффективно работающие в многопоточной среде нежели стандартные универсальные коллекции из java.util пакета. Вместо базового враппера Collections.synchronizedList с блокированием доступа ко всей коллекции используются блокировки по сегментам данных или же оптимизируется работа для параллельного чтения данных по wait-free алгоритмам. ++ Queues — неблокирующие и блокирующие очереди с поддержкой многопоточности. Неблокирующие очереди заточены на скорость и работу без блокирования потоков. Блокирующие очереди используются, когда нужно «притормозить» потоки «Producer» или «Consumer», если не выполнены какие-либо условия, например, очередь пуста или перепонена, или же нет свободного «Consumer»'a. ++ Synchronizers — вспомогательные утилиты для синхронизации потоков. Представляют собой мощное оружие в «параллельных» вычислениях. ++ Executors — содержит в себе отличные фрейморки для создания пулов потоков, планирования работы асинхронных задач с получением результатов. ++ Locks — представляет собой альтернативные и более гибкие механизмы синхронизации потоков по сравнению с базовыми synchronized, wait, notify, notifyAll. ++ Atomics — классы с поддержкой атомарных операций над примитивами и ссылками. + +__Concurrent Collections__ + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc3.png) + + ++ CopyOnWrite коллекции - Название говорит само за себя. Все операции по изменению коллекции (add, set, remove) приводят к созданию новой копии внутреннего массива. Тем самым гарантируется, что при проходе итератором по коллекции не кинется ConcurrentModificationException. Следует помнить, что при копировании массива копируются только референсы (ссылки) на объекты (shallow copy), т.ч. доступ к полям элементов не thread-safe. CopyOnWrite коллекции удобно использовать, когда write операции довольно редки, например при реализации механизма подписки listeners и прохода по ним. + +CopyOnWriteArrayList — Потокобезопасный аналог ArrayList, реализованный с CopyOnWrite алгоритмом. + +CopyOnWriteArraySet — Имплементация интерфейса Set, использующая за основу CopyOnWriteArrayList. В отличии от CopyOnWriteArrayList, дополнительных методов нет. + ++ Scalable Maps - Улучшенные реализации HashMap, TreeMap с лучшей поддержкой многопоточности и масштабируемости. + +ConcurrentMap — Интерфейс, расширяющий Map несколькими дополнительными атомарными операциями. +Дополнительные методы + +ConcurrentHashMap — В отличие от Hashtable и блоков synhronized на HashMap, данные представлены в виде сегментов, разбитых по hash'ам ключей. В результате, для доступ к данным лочится по сегментам, а не по одному объекту. В дополнение, итераторы представляют данные на определенный срез времени и не кидают ConcurrentModificationException. Более детально ConcurrentHashMap описан в хабратопике тут. +Дополнительный конструктор + +ConcurrentNavigableMap — Расширяет интерфейс NavigableMap и вынуждает использовать ConcurrentNavigableMap объекты в качестве возвращаемых значений. Все итераторы декларируются как безопасные к использованию и не кидают ConcurrentModificationException. + +ConcurrentSkipListMap — Является аналогом TreeMap с поддержкой многопоточности. Данные также сортируются по ключу и гарантируется усредненная производительность log(N) для containsKey, get, put, remove и других похожих операций. Алгоритм работы SkipList описан на Wiki и хабре. + +ConcurrentSkipListSet — Имплементация Set интерфейса, выполненная на основе ConcurrentSkipListMap. + +__Queues__ + ++ Non-Blocking Queues - Потокобезопасные и неблокирующие имплементации Queue на связанных нодах (linked nodes). + +ConcurrentLinkedQueue — В имплементации используется wait-free алгоритм от Michael & Scott, адаптированный для работы с garbage collector'ом. Этот алгоритм довольно эффективен и, что самое важное, очень быстр, т.к. построен на CAS. Метод size() может работать долго, т.ч. лучше постоянно его не дергать. Детальное описание алгоритма можно посмотреть тут тут. + +ConcurrentLinkedDeque — Deque расшифровывается как Double ended queue и читается как «Deck». Это означает, что данные можно добавлять и вытаскивать с обоих сторон. Соответственно, класс поддерживает оба режима работы: FIFO (First In First Out) и LIFO (Last In First Out). На практике, ConcurrentLinkedDeque стоит использовать только, если обязательно нужно LIFO, т.к. за счет двунаправленности нод данный класс проигрывает по производительности на 40% по сравнению с ConcurrentLinkedQueue. + ++ Blocking Queues + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc4.png) + + +BlockingQueue — При обработке больших потоков данных через очереди становится явно недостаточно использования ConcurrentLinkedQueue. Если потоки, разгребающие очередь перестанут справляться с наплывом данных, то можно довольно быстро схлопотать out of memory или перегрузить IO/Net настолько, что производительность упадет в разы пока не настанет отказ системы по таймаутам или из за отсутствия свободных дескрипторов в системе. Для таких случаев нужна queue с возможностью задать размер очереди или с блокировками по условиям. Тут то и появляется интерфейс BlockingQueue, открывающий дорогу к целому набору полезных классов. Помимо возможности задавать размер queue, добавились новые методы, которые реагируют по-разному на незаполнение или переполнение queue. Так, например, при добавлении элемента в переполненную queue, один метод кинет IllegalStateException, другой вернет false, третий заблокирует поток, пока не появится место, четвертый же заблокирует поток с таймаутом и вернет false, если место так и не появится. Также стоит отметить, что блокирующие очереди не поддерживают null значения, т.к. это значение используется в методе poll как индикатор таймаута. + +ArrayBlockingQueue — Класс блокирующей очереди, построенный на классическом кольцевом буфере. Помимо размера очереди, доступна возможность управлять «честностью» блокировок. Если fair=false (по умолчанию), то очередность работы потоков не гарантируется. Более подробно о «честности» можно посмотреть в описании ReentrantLock'a. + +DelayQueue — Довольно специфичный класс, который позволяет вытаскивать элементы из очереди только по прошествии некоторой задержки, определенной в каждом элементе через метод getDelay интерфейса Delayed. + +LinkedBlockingQueue — Блокирующая очередь на связанных нодах, реализованная на «two lock queue» алгоритме: один лок на добавление, другой на вытаскивание элемента. За счет двух локов, по сравнению с ArrayBlockingQueue, данный класс показывает более высокую производительность, но и расход памяти у него выше. Размер очереди задается через конструктор и по умолчанию равен Integer.MAX_VALUE. + +PriorityBlockingQueue — Является многопоточной оберткой над PriorityQueue. При вставлении элемента в очередь, его порядок определяется в соответствии с логикой Comparator'а или имплементации Comparable интерфейса у элементов. Первым из очереди выходит самый наименьший элемент. + +SynchronousQueue — Эта очередь работает по принципу один вошел, один вышел. Каждая операция вставки блокирует «Producer» поток до тех пор, пока «Consumer» поток не вытащит элемент из очереди и наоборот, «Consumer» будет ждать пока «Producer» не вставит элемент. + +BlockingDeque — Интерфейс, описывающий дополнительные методы для двунаправленной блокирующей очереди. Данные можно вставлять и вытаскивать с двух сторон очереди. + +LinkedBlockingDeque — Двунаправленная блокирующая очередь на связанных нодах, реализованная как простой двунаправленный список с одним локом. Размер очереди задается через конструктор и по умолчанию равен Integer.MAX_VALUE. + +TransferQueue — Данный интерфейс может быть интересен тем, что при добавлении элемента в очередь существует возможность заблокировать вставляющий «Producer» поток до тех пор, пока другой поток «Consumer» не вытащит элемент из очереди. Блокировка может быть как с таймаутом, так и вовсе может быть заменена проверкой на наличие ожидающих «Consumer»ов. Тем самым появляется возможность реализации механизма передачи сообщений с поддержкой как синхронных, так и асинхронных сообщений. + +LinkedTransferQueue — Реализация TransferQueue на основе алгоритма Dual Queues with Slack. Активно использует CAS и парковку потоков, когда они находятся в режиме ожидания. + +__Synchronizers__ + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc5.png) + + +Semaphore — Семафоры чаще всего используются для ограничения количества потоков при работе с аппаратными ресурсами или файловой системой. Доступ к общему ресурсу управляется с помощью счетчика. Если он больше нуля, то доступ разрешается, а значение счетчика уменьшается. Если счетчик равен нулю, то текущий поток блокируется, пока другой поток не освободит ресурс. Количество разрешений и «честность» освобождения потоков задается через конструктор. Узким местом при использовании семафоров является задание количества разрешений, т.к. зачастую это число приходится подбирать в зависимости от мощности «железа». + +CountDownLatch — Позволяет одному или нескольким потокам ожидать до тех пор, пока не завершится определенное количество операций, выполняющих в других потоках. Классический пример с драйвером довольно неплохо описывает логику класса: Потоки, вызывающие драйвер, будут висеть в методе await (с таймаутом или без), пока поток с драйвером не выполнит инициализацию с последующим вызовом метода countDown. Этот метод уменьшает счетчик count down на единицу. Как только счетчик становится равным нулю, все ожидающие потоки в await продолжат свою работу, а все последующие вызовы await будут проходить без ожиданий. Счетчик count down одноразовый и не может быть сброшен в первоначальное состояние. + +CyclicBarrier — Может использоваться для синхронизации заданного количества потоков в одной точке. Барьер достигается когда N-потоков вызовут метод await(...) и заблокируются. После чего счетчик сбрасывается в исходное значение, а ожидающие потоки освобождаются. Дополнительно, если нужно, существует возможность запуска специального кода до разблокировки потоков и сброса счетчика. Для этого через конструктор передается объект с реализацией Runnable интерфейса. + +Exchanger — Как видно из названия, основное предназначение данного класса — это обмен объектами между двумя потоками. При этом, также поддерживаются null значения, что позволяет использовать данный класс для передачи только одного объекта или же просто как синхронизатор двух потоков. Первый поток, который вызывает метод exchange(...) заблокируется до тех пор, пока тот же метод не вызовет второй поток. Как только это произойдет, потоки обменяются значениями и продолжат свою работу. + +Phaser — Улучшенная реализация барьера для синхронизации потоков, которая совмещает в себе функционал CyclicBarrier и CountDownLatch, вбирая в себя самое лучшее из них. Так, количество потоков жестко не задано и может динамически меняться. Класс может повторно переиспользоваться и сообщать о готовности потока без его блокировки. Более подробно можно почитать в хабратопике тут. + +__Executors__ + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc6.png) + +Здесь будут описаны интерфейсы для запуска асинхронных задач с возможностью получения результатов через Future и Callable интерфейсы, а также сервисы и фабрики для создания thread pools: ThreadPoolExecutor, ScheduledPoolExecutor, ForkJoinPool. + +Future — Замечательный интерфейс для получения результатов работы асинхронной операции. Ключевым методом здесь является метод get, который блокирует текущий поток (с таймаутом или без) до завершения работы асинхронной операции в другом потоке. Также, дополнительно существуют методы для отмены операции и проверки текущего статуса. В качестве имплементации часто используется класс FutureTask. + +RunnableFuture — Если Future — это интерфейс для Client API, то интерфейс RunnableFuture уже используется для запуска асинхронной части. Успешное завершение метода run() завершает асинхронную операцию и позволяет вытаскивать результаты через метод get. + +Callable — Расширенный аналог интерфейса Runnable для асинхронных операций. Позволяет возвращать типизированное значение и кидать checked exception. Несмотря на то, что в этом интерфейсе отсутсвует метод run(), многие классы java.util.concurrent поддерживают его наряду с Runnable. + +FutureTask — Имплементация интерфейса Future/RunnableFuture. Асинхронная операция принимается на вход одного из конструкторов в виде Runnable или Callable объектов. Сам же класс FutureTask предназначен для запуска в worker потоке, например через new Thread(task).start(), или через ThreadPoolExecutor. Результаты работы асинхронной операции вытаскиваются через метод get(...). + +Delayed — Используется для асинхронных задач, которые должны начаться в будущем, а также в DelayQueue. Позволяет задавать время до начала асинхронной операции. + +ScheduledFuture — Маркерный интерфейс, объединяющий Future и Delayed интерфейсы. + +RunnableScheduledFuture — Интерфейс, объединяющий RunnableFuture и ScheduledFuture. Дополнительно можно указывать является ли задача одноразовой или же должна запускаться с заданной периодичностью. + ++ Executor Services + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc7.png) + +Executor — Представляет собой базовый интерфейс для классов, реализующих запуск Runnable задач. Тем самым обеспечивается развязка между добавлением задачи и способом её запуска. + +ExecutorService — Интерфейс, который описывает сервис для запуска Runnable или Callable задач. Методы submit на вход принимают задачу в виде Callable или Runnable, а в качестве возвращаемого значения идет Future, через который можно получить результат. Методы invokeAll работают со списками задач с блокировкой потока до завершения всех задач в переданном списке или до истечения заданного таймаута. Методы invokeAny блокируют вызывающий поток до завершения любой из переданных задач. В дополнении ко всему, интерфейс содержит методы для graceful shutdown. После вызова метода shutdown, данный сервис больше не будет принимать задачи, кидая RejectedExecutionException при попытке закинуть задачу в сервис. + +ScheduledExecutorService — В дополнении к методам ExecutorService, данный интерфейс добавляет возможность запускать отложенные задачи. + +AbstractExecutorService — Абстрактный класс для построения ExecutorService'a. Имплементация содержит базовую имплементацию методов submit, invokeAll, invokeAny. От этого класса наследуются ThreadPoolExecutor, ScheduledThreadPoolExecutor и ForkJoinPool. + ++ ThreadPoolExecutor & Factory + +Executors — Класс-фабрика для создания ThreadPoolExecutor, ScheduledThreadPoolExecutor. Если нужно создать один из этих пулов, эта фабрика именно то, что нужно. Также, тут содержатся разные адаптеры Runnable-Callable, PrivilegedAction-Callable, PrivilegedExceptionAction-Callable и другие. + +ThreadPoolExecutor — Очень мощный и важный класс. Используется для запуска асинхронных задач в пуле потоков. Тем самым практически полностью отсутствует оверхэд на поднятие и остановку потоков. А за счет фиксируемого максимума потоков в пуле обеспечивается прогнозируемая производительность приложения. Как было ранее сказано, создавать данный пул предпочтительно через один из методов фабрики Executors. Если же стандартных конфигураций будет недостаточно, то через конструкторы или сеттеры можно задать все основые параметры пула. Более подробно можно ознакомиться в этом топике. + +ScheduledThreadPoolExecutor — В дополнении к методам ThreadPoolExecutor, позволяет запускать задачи после определенной задержки, а также с некоторой периодичностью, что позволяет реализовать на базе этого класса Timer Service. + +ThreadFactory — По умолчанию, ThreadPoolExecutor использует стандартную фабрику потоков, получаемую через Executors.defaultThreadFactory(). Если нужно что-то больше, например задание приоритета или имени потока, то можно создать класс с реализацией этого интерфейса и передать его в ThreadPoolExecutor. + +RejectedExecutionHandler — Позволяет определить обработчик для задач, которые по каким то причинам не могут быть выполнены через ThreadPoolExecutor. Такой случай может произойти, когда нет свободных потоков или сервис выключается или выключен (shutdown). Несколько стандартных имплементаций находятся в классе ThreadPoolExecutor: CallerRunsPolicy — запускает задачу в вызывающем потоке; AbortPolicy — кидает эксцепшен; DiscardPolicy — игнорирует задачу; DiscardOldestPolicy — удаляет самую старую незапущенную задачу из очереди, затем пытается добавить новую задачу еще раз. + ++ Fork Join + +В java 1.7 появился новый Fork Join фреймворк для решения рекурсивных задач, работающих по алгоритмам разделяй и влавствуй или Map Reduce. Чтобы было более наглядней, можно привести визуальный пример алгоритма сортировки quicksort: + +Так, за счет разбиения на части, можно добиться их параллельной обработки в разных потоках. Для решения этой задачи можно использовать и обычный ThreadPoolExecutor, но за счет частого переключения контекста и отслеживания контроля исполнения все это не очень эффективно работает. Тут то нам приходит на помощь Fork Join framework в основу которого используется work-stealing алгоритм. Наиболее хорошо раскрывает себя в системах с большим количеством процессоров. Подробнее можно ознакомиться в блоге тут или публикации Doug Lea. Про производительность и масштабируемость можно почитать тут. + +ForkJoinPool — Представляет собой точку входа для запуска корневых (main) ForkJoinTask задач. Подзадачи запускаются через методы задачи, от которой нужно отстрелиться (fork). По умолчанию создается пул потоков с количеством потоков равным количеству доступных для JVM процессоров (cores). + +ForkJoinTask — Базовый класс для всех Fork Join задач. Из ключевых методов можно отметить: fork() — добавляет задачу в очередь текущего потока ForkJoinWorkerThread для асинхронного выполнения; invoke() — запускает задачу в текущем потоке; join() — ожидает завершения подзадачи с возвращением результата; invokeAll(...) — объединяет все три предыдущие предыдущие операции, выполняя две или более задач за один заход; adapt(...) — создает новую задачу ForkJoinTask из Runnable или Callable объектов. + +RecursiveTask — Абстрактный класс от ForkJoinTask, с объявлением метода compute, в котором должна производиться асинхронная операция в наследнике. + +RecursiveAction — Отличается от RecursiveTask тем, не возвращает результат. + +ForkJoinWorkerThread — Используется в качестве имплементации по умолчанию в ForkJoinPoll. При желании можно отнаследоваться и перегрузить методы инициализации и завершения worker потока. + ++ Completion Service + +CompletionService — Интерфейс сервиса с развязкой запуска асинхронных задач и получением результатов. Так, для добавления задач используются методы submit, а для вытаскивания результатов завершенных задач используются блокирующий метод take и неблокирующий poll. + +ExecutorCompletionService — По сути является враппером над любым классом, реализующим интерфейс Executor, например ThreadPoolExecutor или ForkJoinPool. Используется преимущественно тогда, когда хочется абстрагироваться от способа запуска задач и контроля за их исполнением. Если есть завершенные задачи — вытаскиваем их, если нет — ждем в take пока что-нибудь не завершится. В основе сервиса по умолчанию используется LinkedBlockingQueue, но может быть передана и любая другая имплементация BlockingQueue. + +__Locks__ + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc8.png) + +Condition — Интерфейс, который описывает альтернативные методы стандарным wait/notify/notifyAll. Объект с условием чаще всего получается из локов через метод lock.newCondition(). Тем самым можно получить несколько комплектов wait/notify для одного объекта. + +Lock — Базовый интерфейс из lock framework, предоставляющий более гибкий подход по ограничению доступа к ресурсам/блокам нежели при использовании synchronized. Так, при использовании нескольких локов, порядок их освобождения может быть произвольный. Плюс имеется возможность пойти по альтернативному сценарию, если лок уже кем то захвачен. + +ReentrantLock — Лок на вхождение. Только один поток может зайти в защищенный блок. Класс поддерживает «честную» (fair) и «нечестную» (non-fair) разблокировку потоков. При «честной» разблокировке соблюдается порядок освобождения потоков, вызывающих lock(). При «нечестной» разблокировке порядок освобождения потоков не гарантируется, но, как бонус, такая разблокировка работает быстрее. По умолчанию, используется «нечестная» разблокировка. + +ReadWriteLock — Дополнительный интерфейс для создания read/write локов. Такие локи необычайно полезны, когда в системе много операций чтения и мало операций записи. + +ReentrantReadWriteLock — Очень часто используется в многопоточных сервисах и кешах, показывая очень хороший прирост производительности по сравнению с блоками synchronized. По сути, класс работает в 2-х взаимоисключающих режимах: много reader'ов читают данные в параллель и когда только 1 writer пишет данные. + +ReentrantReadWriteLock.ReadLock — Read lock для reader'ов, получаемый через readWriteLock.readLock(). + +ReentrantReadWriteLock.WriteLock — Write lock для writer'ов, получаемый через readWriteLock.writeLock(). + +LockSupport — Предназначен для построения классов с локами. Содержит методы для парковки потоков вместо устаревших методов Thread.suspend() и Thread.resume(). + +AbstractOwnableSynchronizer — Базовый класс для построения механизмов сихнронизации. Содержит всего одну пару геттер/сеттер для запоминания и чтения эксклюзивного потока, который может работать с данными. + +AbstractQueuedSynchronizer — Используется в качестве базового класса для механизма синхронизации в FutureTask, CountDownLatch, Semaphore, ReentrantLock, ReentrantReadWriteLock. Может применяться при создании новых механизмов синхронизации, полагающихся на одиночное и атомарное значение int. + +AbstractQueuedLongSynchronizer — Разновидность AbstractQueuedSynchronizer, которая поддерживает атомарное значение long. + +__Atomics__ + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Conc9.png) + +AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray — Что если в классе нужно синхронизировать доступ к одной простой переменной типа int? Можно использовать конструкции с synchronized, а при использовании атомарных операций set/get, подойдет также и volatile. Но можно поступить еще лучше, использовав новые классы Atomic*. За счет использования CAS, операции с этими классами работают быстрее, чем если синхронизироваться через synchronized/volatile. Плюс существуют методы для атомарного добавления на заданную величину, а также инкремент/декремент. + +AtomicReference — Класс для атомарных операцией с ссылкой на объект. + +AtomicMarkableReference — Класс для атомарных операцией со следующей парой полей: ссылка на объект и битовый флаг (true/false). + +AtomicStampedReference — Класс для атомарных операцией со следующей парой полей: ссылка на объект и int значение. + +AtomicReferenceArray — Массив ссылок на объекты, который может атомарно обновляться. + +AtomicIntegerFieldUpdater, AtomicLongFieldUpdater,AtomicReferenceFieldUpdater — Классы для атомарного обновления полей по их именам через reflection. Смещение полей для CAS определяется в конструкторе и кешируются, т.ч. тут нет сильного падения производительности из за reflection. + +[к оглавлению](#Многопоточность) + ## В чем заключаются различия между `CyclicBarrier` и `CountDownLatch`? `CountDownLatch` (замок с обратным отсчетом) предоставляет возможность любому количеству потоков в блоке кода ожидать до тех пор, пока не завершится определенное количество операций, выполняющихся в других потоках, перед тем как они будут «отпущены», чтобы продолжить свою деятельность. В конструктор `CountDownLatch(int count)` обязательно передается количество операций, которое должно быть выполнено, чтобы замок «отпустил» заблокированные потоки. diff --git a/core.md b/core.md index c957888..84a1e6a 100644 --- a/core.md +++ b/core.md @@ -12,10 +12,16 @@ + [Какие побитовые операции вы знаете?](#Какие-побитовые-операции-вы-знаете) + [Autoboxing и unboxing?](#Autoboxing-и-unboxing) + [Дайте определение понятию _«интерфейс»_. Какие модификаторы по умолчанию имеют поля и методы интерфейсов?](#Дайте-определение-понятию-интерфейс-Какие-модификаторы-по-умолчанию-имеют-поля-и-методы-интерфейсов) ++ [Где и для чего используется модификатор `abstract`?](#Где-и-для-чего-используется-модификатор-`abstract`) ++ [Можно ли объявить метод абстрактным и статическим одновременно?](#Можно-ли-объявить-метод-абстрактным-и-статическим-одновременно) + [Чем абстрактный класс отличается от интерфейса? В каких случаях следует использовать абстрактный класс, а в каких интерфейс?](#Чем-абстрактный-класс-отличается-от-интерфейса-В-каких-случаях-следует-использовать-абстрактный-класс-а-в-каких-интерфейс) + [Почему в некоторых интерфейсах вообще не определяют методов?](#Почему-в-некоторых-интерфейсах-вообще-не-определяют-методов) + [Почему нельзя объявить метод интерфейса с модификатором `final`?](#Почему-нельзя-объявить-метод-интерфейса-с-модификатором-final) + [Что имеет более высокий уровень абстракции - _класс_, _абстрактный класс_ или _интерфейс_?](#Что-имеет-более-высокий-уровень-абстракции---класс-абстрактный-класс-или-интерфейс) ++ [Что такое default методы интерфейса?](#Что-такое-default-методы-интерфейса) ++ [Как вызывать default метод интерфейса в реализующем этот интерфейс классе?](#Как-вызывать-default-метод-интерфейса-в-реализующем-этот-интерфейс-классе) ++ [Что такое static метод интерфейса?](#Что-такое-static-метод-интерфейса) ++ [Как вызывать static метод интерфейса?](#Как-вызывать-static-метод-интерфейса) + [Может ли объект получить доступ к члену класса объявленному как `private`? Если да, то каким образом?](#Может-ли-объект-получить-доступ-к-члену-класса-объявленному-как-private-Если-да-то-каким-образом) + [Каков порядок вызова конструкторов и блоков инициализации с учётом иерархии классов?](#Каков-порядок-вызова-конструкторов-и-блоков-инициализации-с-учётом-иерархии-классов) + [Зачем нужны и какие бывают блоки инициализации?](#Зачем-нужны-и-какие-бывают-блоки-инициализации) @@ -111,6 +117,7 @@ + [Предположим, есть метод, который может выбросить `IOException` и `FileNotFoundException` в какой последовательности должны идти блоки `catch`? Сколько блоков `catch` будет выполнено?](#Предположим-есть-метод-который-может-выбросить-ioexception-и-filenotfoundexception-в-какой-последовательности-должны-идти-блоки-catch-Сколько-блоков-catch-будет-выполнено) + [Что если исключение появится в try блоке и в finally](#Что-если-исключение-появится-в-try-блоке-и-в-finally) + [Подавленные исключения](#Подавленные-исключения) ++ [Как создать аннотацию?](#Как-создать-аннотацию) + [Что такое _generics_?](#Что-такое-generics) + [Ковариантность, контравариантность и инвариантность](#Ковариантность-контравариантность-и-инвариантность) + [Wildcards](#Wildcards) @@ -127,6 +134,8 @@ __JDK__, Java Development Kit (Комплект разработки на Java) Коротко: __JDK__ - среда для разработки программ на Java, включающая в себя __JRE__ - среду для обеспечения запуска Java программ, которая в свою очередь содержит __JVM__ - интерпретатор кода Java программ. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Core1.jpg) + [к оглавлению](#java-core) ## Какие существуют модификаторы доступа? @@ -156,6 +165,28 @@ __public__ (публичный): класс/члены класса доступ [к оглавлению](#java-core) +## Типы переменных + ++ boolean: хранит значение true или false ++ byte: хранит целое число от -128 до 127 и занимает 1 байт = 8 бит ++ short: хранит целое число от -32768 до 32767 и занимает 2 байта = 16 бит ++ int: хранит целое число от -2147483648 до 2147483647 и занимает 4 байта = 32 бита ++ long: хранит целое число от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 и занимает 8 байт ++ double: хранит число с плавающей точкой от ±4.9*10-324 до ±1.8*10308 и занимает 8 байт + +double x = 8.5; + ++ float: хранит число с плавающей точкой от -3.4*1038 до 3.4*1038 и занимает 4 байта + +float x = 8.5F; + ++ char: хранит одиночный символ в кодировке UTF-16 и занимает 2 байта, поэтому диапазон хранимых значений от 0 до 65535 + +Классы обертки (Integer, Float, Long и тд) хранят внутри примитив, расширяя его дополнительными методами (наприер .equals()), возможностью хранить null и использоваться в коллекциях + +Метод valueOf() реализован во всех классах-обёртках в Java: +Метод Integer.valueOf(int) использует кеширование значений от –128 до 127 из внутреннего пула. Это оптимизация для часто используемых чисел. Объекты в пуле — это объекты в heap-памяти (куче), как и любые другие Java-объекты. Как и String pool + ## Какими значениями инициализируются переменные по умолчанию? + Числа инициализируются `0` или `0.0`; + `char` — `\u0000`; @@ -253,6 +284,119 @@ short s = 7; // 0000 0000 0000 0111 [к оглавлению](#java-core) +## Как применяют побитовые операции? + ++ Маски (битовые флаги) ++ +Маска — это число, в котором каждый бит используется как отдельный флаг (true/false). Это позволяет хранить несколько булевых значений в одной переменной int или long. + +` +final int READ = 1 << 0; // 0001 = 1 +final int WRITE = 1 << 1; // 0010 = 2 +final int EXECUTE = 1 << 2; // 0100 = 4 + +int rights = READ | WRITE; // 0011 = 3 (чтение + запись) + +// Проверка +boolean canRead = (rights & READ) != 0; // true +boolean canExecute = (rights & EXECUTE) != 0; // false + +// Добавим EXECUTE +rights |= EXECUTE; // теперь 0111 = 7 + +// Уберём WRITE +rights &= ~WRITE; // теперь 0101 = 5 (READ + EXECUTE) +` + +Здесь | используется для добавления флага, +& для проверки, +~ для обнуления конкретного бита. + +В Java часто вместо масок используют EnumSet, но маски полезны, если надо хранить очень много флагов компактно (например, при работе с сетью или драйверами). + +` +enum Permission { + READ(1 << 0), + WRITE(1 << 1), + EXECUTE(1 << 2); + + final int mask; + Permission(int mask) { this.mask = mask; } +} + +` ++ Быстрое умножение/деление на степени двойки + +Сдвиги влево и вправо работают быстрее, чем умножение/деление. + +` +int x = 5 << 1; // 10 (5 * 2) +int y = 20 >> 2; // 5 (20 / 4) + +int z = 7; + +int mul = z << 1; // 14 (7 * 2) +int div = z >> 1; // 3 (7 / 2) +` + ++ Оптимизация в алгоритмах +__Проверка чётности__ +` +int n = 10; +if ((n & 1) == 0) { + System.out.println("Чётное"); +} else { + System.out.println("Нечётное"); +} +` +__Быстрый модуль по степени двойки__ +` +int n = 77; +int mod = n & (8 - 1); // 77 % 8 = 5 +System.out.println(mod); // 5 +` + +__Переключение флага (toggle)__ +` +int flags = 0; + +// переключаем WRITE +flags ^= WRITE; // включили WRITE +flags ^= WRITE; // снова выключили WRITE + +` +__Очистка всех флагов, кроме одного__ +` +int flags = READ | WRITE | EXECUTE; +flags &= READ; // оставляем только READ +` + +__Интересные применения__ + +Хранение состояний: например, в графике (VISIBLE, ENABLED, SELECTED). + +Комбинация параметров: настройки работы функции (FULLSCREEN | VSYNC). + +Быстрая работа с большими массивами булевых значений (экономия памяти). + +[к оглавлению](#java-core) + +## Разница >> и >>> + +>>> и <<< сдвиги без учета знака +` +int x = -8; // 11111111111111111111111111111000 + +System.out.println(x >> 2); // -2 (111111...1110) — арифметический сдвиг +System.out.println(x >>> 2); // 1073741822 (001111...1110) — логический сдвиг + +` +Зачем нужен >>>, если есть >>? + +Ответ: для работы с int/long как беззнаковыми числами (например, при работе с сетевыми протоколами или бинарными файлами). + +[к оглавлению](#java-core) + ## Autoboxing и unboxing? __Автоупаковка__ - автоматическая инкапсуляция примитивного типа в эквивалентную ему класс-обёртку всякий раз, когда требуется объект данного типа. @@ -276,12 +420,30 @@ Unboxing происходит: Использование абстрактных классов и методов позволяет описать некий шаблон объекта, который должен быть реализован в других классах. В них же самих описывается лишь некое общее для всех потомков поведение. +Особенности абстрактных классов: ++ Может быть конструктор (для вызовов по цепочке из наследников) ++ Имплементят интерфейсы, но не обязаны реализовывать их методы ++ Не могут быть final ++ Могут содержать static методы ++ Нельзя создать объект ++ Абстрактные методы могут отсутствовать ++ Может содержать метод main() + +[к оглавлению](#java-core) + +## Можно ли объявить метод абстрактным и статическим одновременно? +Нет. В таком случае компилятор выдаст ошибку: "Illegal combination of modifiers: ‘abstract’ and ‘static’". Модификатор abstract говорит, что метод будет реализован в другом классе, а static наоборот указывает, что этот метод будет доступен по имени класса. + [к оглавлению](#java-core) ## Дайте определение понятию _«интерфейс»_. Какие модификаторы по умолчанию имеют поля и методы интерфейсов? Ключевое слово `interface` используется для создания полностью абстрактных классов. Основное предназначение интерфейса - определять каким образом мы можем использовать класс, который его реализует. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не реализует их поведение. Все методы неявно объявляются как `public`. Начиная с Java 8 в интерфейсах разрешается размещать реализацию методов по умолчанию `default` и статических `static` методов. +Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс. +Статические методы в интерфейсе являются частью интерфейса без возможности использовать их для объектов класса реализации; +Методы класса java.lang.Object нельзя переопределить как статические; +Статические методы в интерфейсе используются для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д. Интерфейс также может содержать и поля. В этом случае они автоматически являются публичными `public`, статическими `static` и неизменяемыми `final`. @@ -289,6 +451,7 @@ Unboxing происходит: ## Чем абстрактный класс отличается от интерфейса? В каких случаях следует использовать абстрактный класс, а в каких интерфейс? + В Java класс может одновременно реализовать несколько интерфейсов, но наследоваться только от одного класса. ++ Интерфейсы описывают только поведение (методы), а состояния (поля) могут быть только константы + Абстрактные классы используются только тогда, когда присутствует тип отношений «is a» (является), то есть класс-наследник расширяет базовый абстрактный класс, а интерфейсы могут быть реализованы разными классами, вовсе не связанными друг с другом + Абстрактный класс - средство, позволяющее избежать написания повторяющегося кода, инструмент для частичной реализации поведения. Интерфейс - это средство выражения семантики класса, контракт, описывающий возможности. Все методы интерфейса неявно объявляются как `public abstract` или (начиная с Java 8) `default` - методами с реализацией по-умолчанию, а поля - `public static final`. Т.е. интерфейс описывает только поведение (методы) объекта, а вот состояний (полей) у него нет (кроме public static final), в то время как у абстрактного класса они могут быть. + Интерфейсы позволяют создавать структуры типов без иерархии. @@ -313,6 +476,47 @@ Unboxing происходит: [к оглавлению](#java-core) +## Что такое default методы интерфейса? +Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово default: +```java +interface Example { + int process(int a); + default void show() { + System.out.println("default show()"); + } +} +``` + +Если класс реализует интерфейс, он может, но не обязан, реализовать методы по-умолчанию, уже реализованные в интерфейсе. Класс наследует реализацию по умолчанию. +Если некий класс реализует несколько интерфейсов, которые имеют одинаковый метод по умолчанию, то класс должен реализовать метод с совпадающей сигнатурой самостоятельно. Ситуация аналогична, если один интерфейс имеет метод по умолчанию, а в другом этот же метод является абстрактным - никакой реализации по умолчанию классом не наследуется. +Метод по умолчанию не может переопределить метод класса java.lang.Object. +Помогают реализовывать интерфейсы без страха нарушить работу других классов. +Позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах. +Дают свободу классам выбрать метод, который нужно переопределить. +Одной из основных причин внедрения методов по умолчанию является возможность коллекций в Java 8 использовать лямбда-выражения. + +[к оглавлению](#java-core) + +## Как вызывать default метод интерфейса в реализующем этот интерфейс классе? +Используя ключевое слово super вместе с именем интерфейса: + Paper.super.show(); + +[к оглавлению](#java-core) + +## Что такое static метод интерфейса? +Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс. +Статические методы в интерфейсе являются частью интерфейса без возможности использовать их для объектов класса реализации; +Методы класса java.lang.Object нельзя переопределить как статические; +Статические методы в интерфейсе используются для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д. + +[к оглавлению](#java-core) + +## Как вызывать static метод интерфейса? +Используя имя интерфейса: + Paper.show(); + +[к оглавлению](#java-core) + ## Может ли объект получить доступ к члену класса объявленному как `private`? Если да, то каким образом? + Внутри класса доступ к приватной переменной открыт без ограничений; + Вложенный класс имеет полный доступ ко всем (в том числе и приватным) членам содержащего его класса; @@ -357,6 +561,9 @@ int fieldValue = (int) field.get(victim); + Блок инициализации способен генерировать исключения, если их объявления перечислены в `throws` всех конструкторов класса. + Блок инициализации возможно создать и в анонимном классе. +Статические поля можно инициализировать при объявлении, в статическом или нестатическом блоке инициализации. +Нестатические поля можно инициализировать при объявлении, в нестатическом блоке инициализации или в конструкторе. + [к оглавлению](#java-core) ## Что означает ключевое слово `static`? @@ -500,7 +707,12 @@ super.method(); Такие категории классов, за исключением первого, также называют внутренними (_Inner class_). Внутренние классы ассоциируются не с внешним классом, а с экземпляром внешнего. -Каждая из категорий имеет рекомендации по своему применению. Если вложенный класс должен быть виден за пределами одного метода или он слишком длинный для того, чтобы его можно было удобно разместить в границах одного метода и если каждому экземпляру такого класса необходима ссылка на включающий его экземпляр, то используется нестатический внутренний класс. В случае, если ссылка на обрамляющий класс не требуется - лучше сделать такой класс статическим. Если класс необходим только внутри какого-то метода и требуется создавать экземпляры этого класса только в этом методе, то используется локальный класс. А, если к тому же применение класса сводится к использованию лишь в одном месте и уже существует тип, характеризующий этот класс, то рекомендуется делать его анонимным классом. +Каждая из категорий имеет рекомендации по своему применению: ++ Не статический: если вложенный класс должен быть виден за пределами одного метода или он слишком длинный для того, чтобы его можно было удобно разместить в границах одного метода и если каждому экземпляру такого класса необходима ссылка на включающий его экземпляр. ++ Статический: если ссылка на обрамляющий класс не требуется. ++ Локальный: если класс необходим только внутри какого-то метода и требуется создавать экземпляры этого класса только в этом методе. ++ Анонимный: если к тому же применение класса сводится к использованию лишь в одном месте и уже существует тип, характеризующий этот класс. + [к оглавлению](#java-core) @@ -721,11 +933,41 @@ Java HotSpot VM предоставляет разработчикам на вы [к оглавлению](#java-core) ## Какие бывают виды ссылок? -+ Сильная ссылка. Она создается каждый раз, когда аллоцируем место в памяти через оператор new. Очистится сборщиком мусора не раньше, чем станет неиспользуемой -+ SoftReference – мягкая ссылка. Объект не станет причиной израсходования всей памяти – гарантированно будет удален до возникновения OutOfMemoryError. Может быть раньше, зависит от реализации сборщика мусора. +В Java существует 4 типа ссылок: сильные (strong reference), мягкие (SoftReference), слабые (WeakReference) и фантомные (PhantomReference). Особенности каждого типа ссылок связаны с работой Garbage Collector. Если объект можно достичь только с помощью цепочки WeakReference (то есть на него отсутствуют сильные и мягкие ссылки), то данный объект будет помечен на удаление. + ++ Strong reference - сильная ссылка. Она создается каждый раз, когда аллоцируем место в памяти через оператор new. Очистится сборщиком мусора не раньше, чем станет неиспользуемой ++ SoftReference – мягкая ссылка. Объект не станет причиной израсходования всей памяти – гарантированно будет удален до возникновения OutOfMemoryError. Может быть раньше, зависит от реализации сборщика мусора. String s = “abc” - переменная s это и есть strong ссылка + WeakReference – слабая ссылка. Слабее мягкой. Не препятствует утилизации объекта, сборщик мусора игнорирует такие ссылки. Если создали слабую связь с объектом, то можем обращаться к нему даже после того, как сильная ссылка обнулилась. Грубо говоря, пока у нас есть слабая ссылка, мы можем вернуть наш объект. ++ +```java +Counter counter = new Counter(); // strong reference +WeakReference weakCounter = new WeakReference(counter); //weak reference +counter = null; // now Counter object is eligible for garbage collection +``` +Теперь, как только вы присвоили strong ссылке counter значение null (counter = null), тот объект что создан в первой строке становится доступным для удаления сборщиком мусора, потому что он больше не имеет strong ссылки. Cозданная Weak ссылка weakCounter не может предотвратить удаление сборщиком объекта Counter. С другой стороны если бы это была Soft ссылка, объект типа Counter не был бы удален до тех пор пока JVM не нуждалась бы в памяти особенно сильно.Soft ссылки в Java представлены классом java.lang.ref.SoftReference. + +Пример создания SoftReference в Java +```java +Counter prime = new Counter(); // prime holds a strong reference +SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object +prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory +``` +После обнуления strong ссылки (в 3-ей строке) на объект Counter останется только 1 мягкая ссылка которая не сможет предотвратить удаление этого объекта сборщиком мусора, но в отличие от weak ссылки сможет отложить этот процесс до тех пор пока не появится острая нехватка памяти. Учитывая это отличие soft ссылки от weak, первая больше подходит для кэшей, а weak для метаданных. Хорошим примером служит класс WeakHashMap который является наследником интерфейса Map как и классы HashMap или TreeMap, но с одной отличительной особенностью. WeakHashMap оборачивает ключи как weak ссылки, что означает что как только не осталось strong ссылок на объект, weak ссылки которые расположены внутри WeakHashMap не спасут от сборщика мусора. + + PhantomReference – фантомная ссылка. Используется для «предсмертной» обработки объекта: объект доступен после финализации, пока не очищен сборщиком мусора. JVM кладет фантомные ссылки в очередь ссылок (reference queue). Получить объект нельзя, как в случае с мягкой ссылкой или слабой, так как здесь метод get( ) возвращает null. + Объект на который указывают только phantom ссылки может быть удален сборщиком в любой момент. Phantom ссылка создается точно так же как weak или soft. +```java +DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference +PhantomReference phantom = new PhantomReference(digit); // phantom reference +digit = null; +``` +Как только вы обнулите strong ссылки на объект DigitalCounter, сборщик мусора удалит его в любой момент, так как теперь на него ведут только phantom ссылки. + +Главное отличие SoftReference от WeakReference в том как сборщик с ними будет работать. Он может удалить объект в любой момент если на него указывают только weak ссылки, с другой стороны объекты с soft ссылкой будут собраны только когда JVM очень нужна память. Благодаря таким особенностям ссылочных классов каждый из них имеет свое применение. SoftReference можно использовать для реализации кэшей и когда JVM понадобится память она освободит ее за счет удаления таких объектов. А WeakReference отлично подойдут для хранения метаданных, например для хранения ссылки на ClassLoader. Если нет классов для загрузки то нет смысла хранить ссылку на ClassLoader, слабая ссылка делает ClassLoader доступным для удаления как только мы назначим ее вместо крепкой ссылки (Strong reference). + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Core2.png) + [к оглавлению](#java-core) ## Опишите алгоритм работы какого-нибудь сборщика мусора реализованного в виртуальной машине HotSpot. @@ -1418,6 +1660,26 @@ public class Example implements AutoCloseable{ ``` Даже, если в методе close() будет сгенерировано исключение и оно добавится к подавленным исключения для блока try, все они заменятся исключением, которое будет сгенерировано блоком finally. +## Как создать аннотацию? +Как видно из примера выше, аннотация определяется описанием с ключевым словом interface и может включать в себя несколько полей, которые можно задать как обязательными, так и не обязательными. В последнем случае подставляется default значение поля. + +Аннотация @Target указывает, что именно мы можем пометить этой аннотацией, это может быть поле, метод, тип и т.д. + +Аннотация @Retention позволяет указать жизненный цикл аннотации: будет она присутствовать только в исходном коде, в скомпилированном файле, или она будет также видна и в процессе выполнения. Выбор нужного типа зависит от того, как вы хотите использовать аннотацию, например, генерировать что-то побочное из исходных кодов, или в процессе выполнения стучаться к классу через reflection. + +Аннотация @Inherited помечает аннотацию, которая будет унаследована потомком класса, отмеченного такой аннотацией. + +```java +import java.lang.annotation.*; +@Target(value=ElementType.FIELD) +@Retention(value= RetentionPolicy.RUNTIME) +public @interface Name { + String name(); + String type() default “string”; +} +``` +[к оглавлению](#java-core) + ## Что такое _generics_? __Generics__ - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры. diff --git a/db.md b/db.md index 24318c2..72dda82 100644 --- a/db.md +++ b/db.md @@ -2,6 +2,8 @@ Нормализация: https://support.microsoft.com/ru-ru/help/283878/description-of-the-database-normalization-basics +Заметки: https://docs.google.com/document/d/1QXEv9hnVRFHaPlPMF3CEIaxL8dQrWYx4MI5PLLj14Cc/edit + # Базы данных + [Что такое _«база данных»_?](#Что-такое-база-данных) + [Что такое _«система управления базами данных»_?](#Что-такое-система-управления-базами-данных) @@ -91,12 +93,20 @@ _Нормализация_ - процесс замены исходной схе ## Какие существуют нормальные формы? __Первая нормальная форма (1NF)__ - Отношение находится в 1NF, если значения всех его атрибутов атомарны (неделимы). ++ В каждой ячейке таблицы должно находиться только 1 значение. ++ Строки не должны повторяться -__Вторая нормальная форма (2NF)__ - Отношение находится в 2NF, если оно находится в 1NF, и при этом все неключевые атрибуты зависят только от ключа целиком, а не от какой-то его части. +__Вторая нормальная форма (2NF)__ - Отношение находится в 2NF, если оно находится в 1NF, и при этом все неключевые атрибуты зависят только от ключа целиком, а не от какой-то его части. Чтобы привести таблицу в 2NF необходимо ее декомпозировать на несколько зависящих друг от друга. ++ Таблица в 1NF. ++ Все атрибуты зависят целиком от primary key,а не от его части. __Третья нормальная форма (3NF)__ - Отношение находится в 3NF, если оно находится в 2NF и все неключевые атрибуты не зависят друг от друга. ++ Таблица в 2NF. ++ Все атрибуты зависят только от primary key, но не от других атрибутов. __Четвёртая нормальная форма (4NF)__ - Отношение находится в 4NF , если оно находится в 3NF и если в нем не содержатся независимые группы атрибутов, между которыми существует отношение «многие-ко-многим». ++ Таблица в 3NF. ++ Ключевые атрибуты не должны зависеть от неключевых. __Пятая нормальная форма (5NF)__ - Отношение находится в 5NF, когда каждая нетривиальная зависимость соединения в ней определяется потенциальным ключом (ключами) этого отношения. @@ -114,18 +124,26 @@ __Денормализация базы данных__ — это процесс [к оглавлению](#Базы-данных) ## Какие существуют типы связей в базе данных? Приведите примеры. -+ __Один к одному__ - любому значению атрибута А соответствует только одно значение атрибута В, и наоборот. ++ __OneToOne ОдинКОдному__ - любому значению атрибута А соответствует только одно значение атрибута В, и наоборот. >Каждый университет гарантированно имеет 1-го ректора: _1 университет → 1 ректор_. -+ __Один ко многим__ - любому значению атрибута А соответствует 0, 1 или несколько значений атрибута В. ++ __OneToMany ОдинКоМногим__ - любому значению атрибута А соответствует 0, 1 или несколько значений атрибута В. >В каждом университете есть несколько факультетов: _1 университет → много факультетов_. -+ __Многие ко многим__ - любому значению атрибута А соответствует 0, 1 или несколько значений атрибута В, и любому значению атрибута В соответствует 0, 1 или несколько значение атрибута А. ++ __ManyToOne МногиеКОдному__ - связь многие к одному, обратная связь для OneToMany + +>Многие университеты находятся в одном городе. + ++ __МногиеКоМногим__ - любому значению атрибута А соответствует 0, 1 или несколько значений атрибута В, и любому значению атрибута В соответствует 0, 1 или несколько значение атрибута А. >1 профессор может преподавать на нескольких факультетах, в то же время на 1-ом факультете может преподавать несколько профессоров: _Несколько профессоров ↔ Несколько факультетов_. +Каждую из которых можно разделить еще на два вида: ++ Bidirectional (пер. - Двунаправленный) - две связи ++ Unidirectional (пер. - Однонаправленный ) — ссылка на связь устанавливается у всех Entity, то есть в случае OneToOne A-B в Entity A есть ссылка на Entity B, в Entity B есть ссылка на Entity A, Entity A считается владельцем этой связи (это важно для случаев каскадного удаления данных, тогда при удалении A также будет удалено B, но не наоборот).Unidirectional - ссылка на связь устанавливается только с одной стороны, то есть в случае OneToOne A-B только у Entity A будет ссылка на Entity B, у Entity B ссылки на A не будет. + [к оглавлению](#Базы-данных) ## Что такое _«индексы»_? Для чего их используют? В чём заключаются их преимущества и недостатки? @@ -267,7 +285,7 @@ __Долговечность (durability)__. Независимо от проб Типичный способ реализации данного уровня изоляции — блокировка данных на время выполнения команды изменения, что гарантирует, что команды изменения одних и тех же строк, запущенные параллельно, фактически выполнятся последовательно, и ни одно из изменений не потеряется. Транзакции, выполняющие только чтение, при данном уровне изоляции никогда не блокируются. -+ __Чтение завершенных транзакций (read committed)__ — На этом уровне обеспечивается защита от чернового, «грязного» чтения, тем не менее, в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных. ++ __Чтение завершенных транзакций (read committed)__ — На этом уровне обеспечивается защита от чернового, «грязного» чтения, тем не менее, в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных. По умолчанию в MySql и PostgreSQL Работает либо через __Блокирование читаемых и изменяемых данных__, т.е. пишущая транзакция блокирует изменяемые данные для читающих транзакций, работающих на уровне read committed или более высоком, до своего завершения, препятствуя, таким образом, «грязному» чтению, а данные, блокируемые читающей транзакцией, освобождаются сразу после завершения операции SELECT (таким образом, ситуация «неповторяющегося чтения» может возникать на данном уровне изоляции). @@ -286,7 +304,7 @@ __Долговечность (durability)__. Независимо от проб При параллельном выполнении транзакций возможны следующие проблемы: + __Потерянное обновление (lost update)__ — когда две транзакции записывают разные значения в одну и ту же ячейку, одно из изменений теряется; -+ __«Грязное» чтение (dirty read)__ — когда читаются данные, которые в этот момент изменяются транзакцией, а потом транзакция откатывается и данные исчезают. ++ __«Грязное» чтение (dirty read)__ — когда читаются данные, которые в этот момент изменяются транзакцией, а потом транзакция откатывается и данные исчезают. В MySQl решается Read Uncommitted, в PostgreSQL - не поддерживается вообще + __Неповторяющееся чтение (non-repeatable read)__ — одна транзакция в ходе своего выполнения несколько раз выбирает множество строк по одним и тем же критериям. Другая транзакция в интервалах между этими выборками меняет или удаляет данные, используемых в критериях выборки первой транзакции, и успешно заканчивается. Из-за этого результаты выборки в первой транзакции будут разными. + __Фантомное чтение (phantom reads)__ — одна транзакция в ходе своего выполнения несколько раз выбирает множество строк по одним и тем же критериям. Другая транзакция в интервалах между этими выборками добавляет новые данные, используемых в критериях выборки первой транзакции, и успешно заканчивается. От неповторяющегося чтения оно отличается тем, что результат повторного обращения к данным изменился не из-за изменения/удаления самих этих данных, а из-за появления новых (фантомных) данных. diff --git a/docker.md b/docker.md new file mode 100644 index 0000000..b6ca703 --- /dev/null +++ b/docker.md @@ -0,0 +1,1095 @@ +[Вопросы для собеседования](README.md) + +https://badtry.net/docker-tutorial-dlia-novichkov-rassmatrivaiem-docker-tak-iesli-by-on-byl-ighrovoi-pristavkoi/ +https://eternalhost.net/base/vps-vds/docker-zapusk-konteynera + +# Docker / Kubernetes ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Зачем _Docker_?](#Зачем-Docker) ++ [Что такое _Docker Image (образ)_?](#Что-такое-Docker-Image-(образ)) ++ [Что такое _Docker контейнер_?](#Что-такое-Docker-контейнер) ++ [Как менять контейнер?](#Как-менять-контейнер) ++ [Что такое _Dockerfile_?](#Что-такое-Dockerfile) ++ [Как пробрасывать локальную папку в контейнер Докера (монтирование папки)?](#Как-пробрасывать-локальную-папку-в-контейнер-Докера-(монтирование папки)) ++ [Что такое Docker Volumes?](#Что-такое-Docker-Volumesr) ++ [Как работают и пробрасываются Docker порты?](#Как-работают-и-пробрасываются-Docker-порты) ++ [Слои Docker образа, прослойка данных и кеширование](#Слои-Docker-образа,-прослойка-данных-и-кеширование) ++ [Что такое Docker-compose?](#Что такое Docker-compose) ++ [Как писать Микро Сервисы с Docker?](#Как-писать-Микро-Сервисы-с-Docker?) ++ [Что такое _Kubernetes_?](#Что-такое-Kubernetes) ++ [Компоненты и архитектура Kubernetes](#Компоненты-и-архитектура-Kubernetes) ++ [Kubectl](#Kubectl) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ + +## Что такое _Docker_? +На сайте Докера можно найти статью (https://www.docker.com/), в которой подробно рассказывается, что такое Docker. Из их слов - это стандартизированное ПО для разработки и развёртывания проектов. + +__Если говорить проще:__ Давайте на секунду забудем про Докер, и вспомним про такую ностальгическую штуку, как GameBoy Color. Если вы помните, игры для этой приставки поставлялись в виде картриджей. И я уверен в том, что производители видео игр пользуются успехом из-за своей простоты: + ++ Когда ты хочешь поиграть, ты просто вставляешь картридж в приставку, и игра сразу же работает. ++ Ты можешь поделиться своей игрой с друзьями, передав всего лишь картридж, который они вставят в приставку, и сразу же смогут играть. + +Docker следует похожему принципу - позволяет запускать своё ПО настолько просто, что это соизмеримо с вставкой картриджа и нажатием кнопки ON на приставке. Это основная суть, почему Docker настолько полезен - теперь кто угодно, у кого установлен Docker может запустить ваше приложение, выполнив для этого всего несколько команд. Раньше, вы, создавая приложения, к примеру на PHP, устанавливали локально PHP, MySql, возможно, NodeJs, при этом устанавливая зависимости в виде нужных расширений и библиотек. И, в случае передачи вашего скрипта какому-то знакомому, ему требовалось настраивать аналогичное окружение, аналогичных версий, иметь аналогичные расширения и конфигурацию, чтобы успешно запустить ваше приложение. Сейчас же, при использовании Докера, такой проблемы не возникнет впринципе. Теперь вам достаточно иметь установленную программу Docker, которая по одной вашей команде установит окружение, описанное в конфиге для запуска вашего приложения. + +Какое программное обеспечение можно запустить с помощью докера? В техническом плане, Docker чем-то похож на виртуальную машину: + +Докер - это движок, который запускает виртуальную операционную систему, имеющую чрезвычайно маленький вес (в отличие от Vagrant-а, который создаёт полноценную виртуальную ОС, Докер, имеет особые образы ПО, запускающиеся в виртуальной среде, не создавая полную копию ОС). Docker позволяет запустить ОС Linux в изолированной среде очень быстро, в течение нескольких минут. + +[к оглавлению](#Docker) + +## Зачем _Docker_? +__Кошмар при установке ПО__, с которым приходится сталкиваться. У вас когда-нибудь было такое, что вы пытаетесь установить ПО на ваш компьютер, а оно отказывается работать? Вы получаете несколько непонятных вам ошибок, из-за которых ничего не запускается. И после нескольких часов гугления, на десятой странице гугла...и на каком-то форуме, до этого неизвестного вам, вы наконец-то находите случайный комментарий, который помогает исправить вашу проблему. + +Аналогично, что делает написание PC игр более сложным, чем написание Game Boy игр - это то, что приходится проектировать систему с учётом большого множества существующих PC девайсов и спецификаций. Так как разные компьютеры имеют различные операционные системы, драйвера, процессоры, графические карты, и т.д. +И потому задача разработчика - написать приложение совместимое со всеми популярными системами, является достаточно затруднительной и трудоёмкой. + +Docker, как и Game Boy приставка, __берёт стандартизированные части программного обеспечения и запускает их__ так, как Game Boy запускал бы игру. + +В этом случае вы не должны беспокоиться об операционной системе, на которой пользователь будет запускать ваше приложение. Теперь, когда пользователи будут запускать приложение через Docker - конфигурация будет собрана автоматически, и код будет выполняться ВСЕГДА. + +__Как разработчик__, теперь вы не должны волноваться о том, на какой системе будет запущено ваше приложение. +__Как пользователь__, вам не нужно волноваться о том, что вы скачаете неподходящую версию ПО (нужного для работы программы). В Докере эта программа будет запущена в аналогичных условиях, при которых это приложение было разработано, потому, исключается факт получить какую-то новую, непредвиденную ошибку. Для пользователя все действия сводятся к принципу подключи и играй. + +[к оглавлению](#Docker) + +## Что такое _Docker Image (образ)_? +Docker образ (он же Docker Image), похож на Game Boy картридж - это просто программное обеспечение. Это стандартизированное программное обеспечение, которое запускается на любой приставке Game Boy. Вы можете дать игру вашему другу, и он сможет просто вставить картридж в приставку, и играть. + +Как в случае с картриджами, бывают различные игры, так и Docker имеет различные образы ПО: ubuntu, php (который наследуется от оригинального образа Ubuntu), nodejs, и т.д. + +```java +docker pull IMAGE_NAME // где - имя скачиваемого образа + +docker pull ubuntu:18.10 +``` +Эта команда сообщает Докеру о том, что нужно скачать образ Ubuntu 18.10 с Dockerhub.com - основной репозиторий Docker-образов, на котором вы и можете посмотреть весь их список и подобрать нужный образ для вашей программы. Представленные в хабе образы можно найти при помощи команд docker и search. К примеру, найти образ Ubuntu можно следующим образом: +````java +docker search ubuntu +```` + +Теперь, для того, чтобы посмотреть список всех загруженных образов, нужно выполнить: +````java +docker images +```` +Проводя аналогии, команда docker images выглядит как коллекция картриджей от приставки, которые у вас есть сейчас + +[к оглавлению](#Docker) + + +## Что такое _Docker контейнер_? +Теперь представьте, что мы обновили нашу приставку с Game Boy на GameCube. Игры хранятся на диске, который предназначен только для чтения самого образа игры. А прочие файлы (сохранения, кеш и т.д.) сохраняются внутри самой приставки, локально. + +Так же, как и игра на диске, исходный Docker Image (образ) - __неизменяемый__. + +Docker контейнер - это экземпляр запущенного образа. Аналогично тому, что вы вставляете диск в приставку, после чего игра начинается. А сам образ игры никак не модифицируется, все файлы, содержащие изменения хранятся где-то локально на приставке. + +Запуск Docker контейнера соответствует тому, что вы играете в свою Gamecube игру. Docker запускает ваш образ в своей среде, аналогично тому, как Gamecube запускает игру с диска, не модифицируя оригинальный образ, а лишь сохраняя изменения и весь прогресс в какой-то песочнице. + +Для запуска контейнера существует команда: +````java +docker run <опциональная команды, которая выполнится внутри контейнера> +```` + +__Давайте запустим наш первый контейнер Ubuntu__ +````java +docker run ubuntu:18.10 echo 'hello from ubuntu' +```` +Команда echo 'hello from ubuntu' была выполнена внутри среды Ubuntu. Другими словами, эта команда была выполнена в контейнере ubuntu:18.10. + +Теперь выполним команду для проверки списка запущенных контейнеров: +````java +docker ps +```` +Здесь пустота... это потому что docker ps показывает только список контейнеров, которые запущены в данный момент (наш же контейнер выполнил одну команду echo 'hello from ubuntu' и завершил свою работу). + +А для того, чтобы посмотреть список всех контейнеров без исключения, нужно добавить флаг -a, выполним: +````java +docker ps -a +```` + +После выполнения нужных операций внутри контейнера, то Docker-контейнер завершает работу. Это похоже на режим сохранения энергии в новых игровых консолях - если вы не совершаете действий какое-то время, то система выключается автоматически. + +Каждый раз, когда вы будете выполнять команду docker run, будет создаваться новый контейнер, на каждую из выполненных команд. + +__Выполнение неограниченное количество команда внутри контейнера__ + +Давайте добавим немного интерактивности в наше обучение. Мы можем подключиться к консоли виртуальной ОС (Ubuntu 18.10), и выполнять любое количество команд без завершения работы контейнера, для этого, запустим команду: +````java +docker run -it ubuntu:18.10 /bin/bash +```` +Опция -it вместе с /bin/bash даёт доступ к выполнению команд в терминале внутри контейнера Ubuntu. + +Теперь, внутри этого контейнера можно выполнять любые команды, применимые к Ubuntu. Вы же можете представлять это как мини виртуальную машину, условно, к консоли которой мы подключились по SSH. + +__Узнаём ID контейнера__ +Иногда является очень полезным узнать ID контейнера, с которым мы работаем. И как раз-таки, при выполнении команды docker run -it /bin/bash, мы окажемся в терминале, где все команды будут выполняться от имени пользователя root@. + +Теперь, все команды буду выполняться внутри операционной системы Ubuntu. Попробуем, например, выполнить команду ls, и посмотрим, список директорий, внутри этого образа Ubuntu.docker-ubuntu-ls + +Docker контейнер является полностью независимым от системы хоста, из которой он запускался. Как изолированная виртуальная машина. И в ней вы можете производить любые изменения, которые никак не повлияют на основную операционную систему. + +Это аналогично тому, как, если бы вы играли в Mario Kart на приставке Gamecube, и неважно, что вы делаете в игре, вы никак не сможете изменить само ядро игры, или изменить информацию, записанную на диске. + +Контейнер является полностью независимым и изолированным от основной операционной системы, аналогично виртуальной операционной системе. Вы можете вносить любые изменения внутри виртуалки, и никакие из этих изменений не повлияют на основную операционную систему. + +Теперь откройте новое окно терминала (не закрывая и не отключаясь от текущего), и выполните команду docker ps +docker-ps-new-window + +Только на этот раз вы можете увидеть, что контейнер с Ubuntu 18.10 в текущий момент запущен. + +Теперь вернёмся назад к первому окну терминала (который находится внутри контейнера), и выполним: +````java +mkdir /truedir //создаст папку truedir +exit //выйдет из контейнера, и вернётся в основную ОС +```` +Выполнив команду exit, контейнер будет остановлен (чтобы убедиться, можете проверить командой docker ps). Теперь, вы так же знаете, как выйти из Docker контейнера. + +Теперь, попробуем ещё раз просмотреть список всех контейнеров, и убедимся, что новый контейнер был создан docker ps -adocker-ps--a2 + +Так же, для того, чтобы запустить ранее созданный контейнер, можно выполнить команду docker start , + +где CONTAINER_ID - id контейнера, который можно посмотреть, выполнив команду docker ps -a (и увидеть в столбце CONTAINER_ID) + +В моём случае, CONTAINER_ID последнего контейнера = 7579c85c8b7e (у вас же, он будет отличаться) + +Запустим контейнер командой: +````java +docker start 7579c85c8b7e //ваш CONTAINER_ID +docker ps +docker exec -it 7579c85c8b7e /bin/bash //ваш CONTAINER_ID +```` +И теперь, если внутри контейнера выполнить команду ls, то можно увидеть, что ранее созданная папка truedir существует в этом контейнереdocker-exex-truedir + +Команда exec позволяет выполнить команду внутри запущенного контейнера. В нашем случае, мы выполнили /bin/bash, что позволило нам подключиться к терминалу внутри контейнера. + +Для выхода, как обычно, выполним exit. + +Теперь остановим и удалим Docker контейнеры командами: +````java +docker stop +docker rm +docker ps a // просмотрим список активных контейнеров +docker stop aa1463167766 // остановим активный контейнер +docker rm aa1463167766 // удалим контейнер +docker rm bb597feb7fbe // удалим второй контейнер +```` +В основном, нам не нужно, чтобы в системе плодилось большое количество контейнеров. Потому, команду docker run очень часто запускают с дополнительным флагом --rm, который удаляет запущенный контейнер после работы: +```java +docker run -it --rm ubuntu:18.10 /bin/bash +``` + +[к оглавлению](#Docker) + +## Как менять контейнер? +Во время запуска контейнера из существующего образа у пользователя есть возможность создавать или удалять файлы, аналогично работе на виртуальной машине. При этом изменения будут распространяться только в определенном контейнере. Доступна и возможность запуска с последующей остановкой контейнера, но после его удаления с помощью docker rm будут утеряны внесенные изменения. + +Соответственно, следует ознакомиться со способом сохранения текущего контейнера как нового образа. + +По завершении инсталляции Node.js в контейнере Ubuntu, на компьютере работает загруженный из образа контейнер. При этом он будет отличаться от использованного для его создания образа. В свою очередь, пользователю может понадобиться уже контейнер Node.js, чтобы использовать его при создании для новых образов. + +Соответственно, следует сохранить результаты в текущем образе предложенной ниже командой: +```java +docker commit -m "What you did to the image" -a "Author Name" container_id repository/new_image_name +``` +Добавление опции -m дает возможность указать сообщение подтверждения. Это позволит будущим пользователям образа понять, что именно было изменено. Что касается параметра -a — с его помощью можно указать, кто его создатель. container_id является тем же идентификатором, который был использован ранее, во время запуска интерактивной сессии в Docker. + +К примеру, с именем пользователя admin и идентификатором 2c8ec46adae1 команда должна иметь следующий вид: +```java +docker commit -m "added Node.js" -a "admin" 2c8ec46adae1 admin/ubuntu-nodejs +``` +Как запустить Docker контейнер на Linux + +После того, как образ будет подтвержден (commit) он сохраняется на компьютере локально. + +Завершающий этап — сохранение созданных образов в базу Docker Hub или другой репозиторий, откуда их может скачать любой желающий. Чтобы получить такую возможность, предварительно нужно создать аккаунт. + +Отправка образов в репозиторий начинается с авторизации на Docker Hub. +```java +docker login -u docker-registry-username +``` + +Чтобы вход был успешно осуществлен, потребуется ввести пароль Docker Hub. Если он правильный, авторизация пройдет успешно. + +Здесь важно знать, что если в реестре Docker имя пользователя отличается от локального, используемого при создании образа, обязательно нужно привязать этот образ к имени учетной записи в хабе. На примере контейнера с NodeJS команда привязки будет выглядеть так: +```java +docker tag admin/ubuntu-nodejs docker-registry-username/ubuntu-nodejs +``` + +После чего можно приступать к загрузке образа на сервер: +```java +docker push docker-registry-username/docker-image-name +``` + +__Автозагрузка контейнеров__ + +Часто встречается ситуация, когда контейнеры останавливаются вследствие определенных факторов. Простейший пример – произошла перезагрузка сервера. Чтобы избавиться от необходимости вручную запускать их, можно настроить автозапуск контейнеров. Для этого следует создать текстовые файлы со специальным форматом для сервиса systemcmd. Рассмотрим пример их создания на примере контейнера my-db, введя в терминал команду: +```java +cat /etc/systemd/system/my-db.service +``` +В пустой файл необходимо добавить следующий код и сохранить его: +```java +[Unit] +Description=MY DB (PG) docker container +Requires=docker.service +After=docker.service +[Service] +Restart=always +ExecStart=/usr/bin/docker start -a my-db +ExecStop=/usr/bin/docker stop -t 2 my-db +TimeoutSec=30 +[Install] +WantedBy=multi-user.target +``` + +После этого остается перезапустить демон systemcmd и включить автозагрузку контейнера mydb, набрав в терминале поочередно команды: +```java +systemctl daemon-reload +systemctl start my-db.service +systemctl enable my-db.service +``` + +[к оглавлению](#Docker) + +## Что такое _Dockerfile_? +Docker позволяет вам делиться с другими средой, в которой ваш код запускался и помогает в её простом воссоздании на других машинах. + +Dockerfile - это обычный конфигурационный файл, описывающий пошаговое создание среды вашего приложения. В этом файле подробно описывается, какие команды будут выполнены, какие образы задействованы, и какие настройки будут применены. А движок Docker-а при запуске уже распарсит этот файл (именуемый как Dockerfile), и создаст из него соответствующий образ (Image), который был описан. + +К примеру, если вы разрабатывали приложение на php7.2, и использовали ElasticSearch 9 версии, и сохранили это в Dockerfile-е, то другие пользователи, которые запустят образ используя ваш Dockerfile, получат ту же среду с php7.2 и ElasticSearch 9. + +С Dockerfile вы сможете подробно описать инструкцию, по которой будет воссоздано конкретное состояние. И делается это довольно-таки просто и интуитивно понятно. + +Если вам нужно воспроизвести среду (образ) на другом ПК, с докером вы так же имеете два варианта при создании образа: + ++ Вы можете запаковать ваш контейнер, создать из него образ (аналогично тому, что вы запишите на диск новую игру с собственными модификациями). Это похоже на способ, когда вы делитесь сохранениями напрямую. ++ Или же, можно описать Dockerfile - подробную инструкцию, которая приведёт среду к нужному состоянию. + +Я склоняюсь ко второму варианту, потому что он более подробный, гибкий, и редактируемый (вы можете переписать Dockerfile, но не можете перемотать состояние образа в случае прямых изменений). + +Пришло время попрактиковаться на реальном примере. Для начала, создадим файл cli.php в корне проекта с содержимым: +````php + --tag + - путь к файлу Dockerfile (. - текущая директория), + - имя, под которым образ будет создан +```` + +Выполним: +````java +docker build . --tag pyramid +```` +При том, что имя файла Dockerfile при указывании пути упускается, нужно указывать только директорию, в которой этот файл находится (а . означает, что файл находится в той директории, из которой была запущена консоль) + +После того, как команда выполнилась, мы можем обращаться к образу по его имени, которое было указано в , проверим список образов: docker images + +Теперь, запустим контейнер из нашего образа командой docker run pyramid + +__Сначала мы скопировали файл cli.php в Docker образ, который создался с помощью Dockerfile. Для того, чтобы удостовериться в том, что файл действительно был проброшен внутрь контейнера, можно выполнить команду docker run pyramid ls, которая в списке файлов покажет и cli.php.__ + +Однако, сейчас этот контейнер недостаточно гибкий. Нам бы хотелось, чтобы можно было удобно изменять количество строк, из скольки состоит пирамида. + +Для этого, отредактируем файл cli.php, и изменим, чтобы количество аргументов принималось из командной строки. Отредактируем вторую строку на: +````php +$n = $i = $argv[1] ?? 5; //а было $n = $i = 5 +// это значит, что мы принимаем аргумент из консоли, а если он не получен, то используем по-умолчанию 5 +```` +После чего, пересоберём образ: docker build . --tag pyramid +И запустим контейнер: docker run pyramid php /cli.php 9, получив вывод ёлки пирамиды в 9 строк + +Почему это работает? +Когда контейнер запускается, вы можете переопределить команду записанную в Dockerfile в поле CMD. + +Наша оригинальная CMD команда, записанная в Dockerfile php /cli.php - будет переопределена новой php /cli.php 9. +Но, было бы неплохо передавать этот аргумент самому контейнеру, вместо переписывания всей команды. Перепишем так, чтобы вместо команды php /cli.php 7 можно было передавать просто аргумент-число. + +Для этого, дополним Dockerfile: +````java +FROM php:7.2-cli +COPY cli.php /cli.php +RUN chmod +x /cli.php +ENTRYPOINT ["php", "/cli.php"] +## аргумент, который передаётся в командную строку +CMD ["9"] +```` +Мы немного поменяли формат записи. В таком случае, CMD будет добавлена к тому, что выполнится в ENTRYPOINT. + +["php", "/cli.php"] на самом деле запускается, как php /cli.php. И, учитывая то, что CMD будет добавлена после выполнения текущей, то итоговая команда будет выглядеть как: php /cli.php 9 - и пользователь сможет переопределить этот аргумент, передавая его в командную строку, во время запуска контейнера. + +Теперь, заново пересоберём образ +````java +docker build . --tag pyramid +```` +И запустим контейнер с желаемым аргументом +````java +docker run pyramid 3 +```` +[к оглавлению](#Docker) + + +## Как пробрасывать локальную папку в контейнер Докера (монтирование папки)? +Монтирование директории в Docker контейнер - это предоставление доступа контейнеру на чтение содержимого вашей папки из основной операционной системы. Помимо чтения из этой папки, так же, контейнер может её изменять, и такая связь является двусторонней: при изменении файлов в основной ОС изменения будут видны в контейнере, и наоборот. + +__Монтирование директории в контейнер позволяет ему читать и писать данные в эту директорию, изменяя её состояние.__ + +Для того, чтобы смонтировать папку из основной системы в контейнер, можно воспользоваться командой +docker run -v : ..., +где DIRECTORY - это путь к папке, которую нужно смонтировать, +CONTAINER_DIRECTORY - путь внутри контейнера. + +Только путь к монтируемой папке должен быть прописан полностью: C:\projects\docker-example, или на *nix-системах можно воспользоваться конструкцией $(pwd) + +Выполним команду: +````java +docker run -it -v C:\projects\docker-example\cli:/mounted ubuntu:18.10 /bin/bash +ls +ls mounted +touch mounted/testfile +```` +При выполнении этой команды, указанная папка смонтируется в папку /mounted, внутри файловой системы контейнера, а команда touch mounted/testfile создаст новый файл под названием testfile, который вы можете увидеть из основной ОС. + +Теперь вы можете увидеть, что после выполнения этой команды в текущей директории появился новый файл testfile. И это говорит о том, что двусторонняя связь работает - при изменении директории на основной ОС всё отразится на смонтированную папку внутри контейнера, а при изменениях изнутри контейнера всё отразится на основную ОС. + +Монтирование папки позволяет вам изменять файлы вашей основной системы прямо во время работы внутри Docker контейнера. + +Это удобная особенность, которая позволяет нам редактировать код в редакторе на основной ОС, а изменения будут сразу же применяться внутри контейнера. +[к оглавлению](#Docker) + + +## Что такое Docker Volumes? +С Docker Volum-ами мы имеем контейнер, который хранит постоянные данные где-то на нашем компьютере (это актуально, потому что после завершения работы контейнер удаляет все пользовательские данные, не входящие в образ). Вы можете прикрепить Volume-данные к любому из запущенных контейнеров. + +Вместо того, чтобы каждый раз, при запуске контейнера, писать, какие из папок вы хотите смонтировать, вы просто можете создать один контейнер с общими данными, который потом будете прикреплять. + +В Dockerfile прописывается параметр volumes: и указывается название переменной и путь к данным, которые хотим сохранять (внутри конкретного контейнера). Вне контейнера можно указать значения по умолчанию +```java +services: + php: + volumes: + - bddata:/var/lib/postgresql/data/ + +volumes: + bddata: null + +``` +Лично я, не использую это очень часто на практике, потому что есть много других методов по управлению данными. Однако, это может быть очень полезно для контейнеров, которые должны сохранять какие-то важные данные, или данные, которыми нужно поделиться между несколькими контейнерами. + +[к оглавлению](#Docker) + + +## Как работают и пробрасываются Docker порты? + +Docker позволяет нам получить доступ к какому-то из портов контейнера, пробросив его наружу (в основную операционную систему). По умолчанию, мы не можем достучаться к каким-либо из портов контейнера. Однако, в Dockerfile опция EXPOSE позволяет нам объявить, к какому из портов мы можем обратиться из основной ОС. + +Для этого, на по-быстрому, запустим Docker-образ php-apache, который работает на 80 порту. + +Для начала, создадим новую папку apache (перейдём в неё cd apache), в которой создадим файл index.php, на основе которого мы и поймём, что всё работает. +````php +: +```` +И мы можем указать любое соответствие портов, но сейчас просто укажем, что порт системы 80 будет слушать 80 порт контейнера: +````java +docker run -p 80:80 own_php_apache +```` +Здесь, вы уже наверное заметили, что добавился новый параметр -p 80:80, который говорит Docker-у: я хочу, чтобы порт 80 из apache был привязан к моему локальному порту 80. + +И теперь, если перейти по адресу localhost:80, то должны увидеть успешный ответ: +[к оглавлению](#Docker) + + +## Слои Docker образа, прослойка данных и кеширование +Каждый раз, когда вы собираете образ, он кешируется в отдельный слой. Ввиду того, что образы являются неизменяемыми, их ядро никогда не модифицируются, потому применяется система кеширования, которая нужна для увеличения скорости билдинга. + +Каждая команда в Dockerfile сохраняется как отельный слой образа. + +Рассмотрим это на примере нашего прошлого Dockerfile-а: +````java +FROM php:7.2-apache +# Копирует код ядра +COPY . /var/www/html +WORKDIR /var/www/html +EXPOSE 80 +```` +Когда вы пишите свой Dockerfile, вы добавляете слои поверх существующего основного образа (указанного в FROM), и создаёте свой собственный образ (Image). + ++ FROM: говорит Докеру взять за основу этот существующий образ. А все новые команды будут добавлены слоями поверх этого основного образа. ++ COPY: копирует файлы с основной ОС в образ ++ WORKDIR: устанавливает текущую папку образа в /var/www/html + +Слой Образа Докера это как точка сохранения в игре Super Mario. Если вы хотите изменить какие-то вещи, произошедшие до этой точки сохранения, то вам придётся перезапустить этот уровень полностью. Если вы хотите продолжить прогресс прохождения, вы можете начать с того места, где остановились. + +Docker начинает кешировать с "того места, где остановился" во время билдинга Dockerfile. Если в Докерфайле не было никаких изменений с момента последнего билдинга, то образ будет взят полностью из кеша. Если же вы измените какую-то строку в Dockerfile - кеш будет взят только тех слоёв команд, которые находятся выше изменённой команды. + +Для иллюстрации этого, добавим новые строки в Dockerfile: +````java +FROM php:7.2-apache +WORKDIR /var/www/html +# Copy the app code +COPY . /var/www/html +RUN apt-get update && apt-get upgrade -y && apt-get install -y curl php7.2-mbstring php7.2-zip php7.2-intl php7.2-xml php7.2-json php7.2-curl +RUN echo "Hello, Docker Tutorial" +EXPOSE 80 +```` +После чего, пересоберём образ: +````java +docker build . --tag own_php_apache +```` +Выполнив эту команду, из вывода в консоль можете увидеть, что некоторые слои были взяты из кеша. Это как раз те команды, выше которых в Dockerfile не было добавлено/изменено содержимого. + +И можно заметить, что в случае изменения Dockerfile, билдинг занимает больше времени, потому что не используется кеш. Где бы вы не написали команду, все закешированные команды, которые находятся ниже в Dockerfile, будут перебилжены заново. А те, что находятся выше, будут по-прежнему браться из кеша. + +Когда вы используете команду COPY, она копирует указанную директорию в контейнер. И, в случае изменения содержимого любого из файлов этой директории, кеш команды COPY будет сброшен. Docker сверяет изменения во время билдинга в каждом из файлов. Если они были изменены, кеш будет сброшен, как и для всех последующих слоёв. + +Если честно, то это действительно крутая функция. Docker следит за изменениями в файлах и использует кеш всегда, когда это нужно (когда были произведены изменения в каких-то из файлов). Изменение ваших файлов потенциально может затрагивать будущие команды, из-за чего, и все последующие слои билдятся заново, а не берутся из кеша. + +Какие выводы из этого можно сделать: + ++ Команды, которые вероятнее всего не будут меняться в будущем, нужно помещать как можно выше в Dockerfile. ++ Команды копирования данных нужно помещать ниже, потому что файлы при разработке изменяются довольно часто. ++ Команды, которые требуют много времени на билдинг, нужно помещать выше. + ++ В заключение, так же хочу сказать, как можно уменьшить размер слоёв Docker образов. +В Dockerfile вы можете иметь несколько команд (RUN) на выполнение: +```java +RUN apt-get update +RUN apt-get install -y wget +RUN apt-get install -y curl +``` + +В результате выполнения этой команды, будет создано 3 разных слоя в образе. Вместо этого, все команды стараются объединить в одну строку: +```java +RUN apt-get update && apt-get install -y wget curl +``` +Если команда становится длинной, и нечитаемой, то для переноса на следующую строку делаем так: +```java +RUN apt-get update && apt-get install -y wget curl && \ +&& apt-get clean -y \ +&& docker-php-ext-install soap mcrypt pdo_mysql zip bcmath +``` +Если же команда становится слишком большой, и неудобной для чтения, то можно создать новый shell скрипт, в который поместить длинную команду, и запускать этот скрипт одной простой командой RUN. + +Технически, только команды ADD, COPY, и RUN создают новый слой в Docker образе, остальные команды кешируются по-другому (https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers) +[к оглавлению](#Docker) + + +## Что такое Docker-Compose? +Инструмент Docker Compose входит в комплект официального программного обеспечения для Docker. Он позволяет решать различные задачи, связанные с управлением одновременно несколькими контейнерами, составляющих в целом один проект. + +Самый очевидный пример – веб-сайт, где для авторизации пользователей необходимо подключение к базе данных. Для такого проекта нужно два сервиса – один отвечает за функционирование сайта, а другой за базу данных. + +Docker-compose написан в формате YAML который по своей сути похож на JSON или XML. Но YAML имеет более удобный формат для его чтения, чем вышеперечисленные. В формате YAML имеют значения пробелы и табуляции, именно пробелами отделяются названия параметров от их значений. + +Создадим новый файл docker-compose.yml, для рассмотрения синтаксиса Docker Compose: +```java +version: '3' + +services: +app: +build: +context: . +ports: +- 8080:80 +``` + +Теперь, построчно разберёмся с заданными параметрами, и что они значат: ++ version: какая версия docker-compose используется (3 версия - самая последняя на даный момент). ++ services: контейнеры которые мы хотим запустить. ++ app: имя сервиса, может быть любым, но желательно, чтобы оно описывало суть этого контейнера. ++ build: шаги, описывающие процесс билдинга. ++ context: где находится Dockerfile, из которого будем билдить образ для контейнера. ++ ports: маппинг портов основной ОС к контейнеру. + +```java +docker-compose build - собирает проект из Dockerfile +docker-compose up - запускает все сервисы контейнера +docker-compose down - останавливает все сервисы контейнера +Ctrl+C для остановки контейнера +``` + +[к оглавлению](#Docker) + + +## Как писать Микро Сервисы с Docker? Что такое микросервисы? + +[к оглавлению](#Docker) + + +## Что-такое-Kubernetes +Это платформа с открытым исходным кодом для развертывания, масштабирования, управления и контроля контейнеризованных приложений либо сервисов. + +__Kubernetes делает следующее:__ ++ Управляет и запускает контейнеры. ++ Балансирует сетевой трафик между узлами кластера Kubernetes и количеством реплик контейнеров. Можно раскатить один образ на несколько Воркер нодов, получив разные IP порты и DNS имя, а кубер сделает Load Balancing между этими контейнерами ++ Осуществляет контроль состояния, автоматические развертывания и откаты реплик контейнеров внутри узлов кластера Kubernetes. ++ Осуществляет распределение нагрузки между узлами кластера Kubernetes. Например управление ресурсами при развертывания нового контейнера ++ Предоставляет автоматическое монтирование систем хранения для контейнеров. Например прикрепить локальный или облачный диск к одному или нескольким докер контейнерам ++ Предоставляет декларативный API и CLI для управления ++ И еще множество полезных, и не очень, модулей и сервисов, которые можно развернуть для управления автоматизацией, инфраструктурой и контейнерами + +__Kubernetes не делает следующее:__ ++ Не собирает контейнеры с исходным кодом вашего приложения или сервиса ++ Не предоставляет процессы и решения непрерывной интеграции (CI) ++ Не включает в себя решения и системы сбора журналов и метрик ++ Не включает в себя решения и системы хранения данных. Только подключение сторонних ++ Не включает в себя решения и системы хранения контейнеров (registry) ++ Не включает в себя решения и системы от всех бед и болячек инфраструктуры + +Kubernetes или K8S — это не просто система оркестрации. Техническое определение оркестрации — это выполнение определенного рабочего процесса: сначала сделай A, затем B, затем C. Напротив, Kubernetes содержит набор независимых, компонуемых процессов управления, которые непрерывно переводят текущее состояние к предполагаемому состоянию. Неважно, как добраться от А до С. Не требуется также и централизованный контроль. Это делает систему более простой в использовании, более мощной, надежной, устойчивой и расширяемой. + +Основной фактор использования Kubernetes в технологических компаниях, где ведется активная разработка приложений, - это гибкий подход к разработке. Сегодня подход в построении архитектуры приложений изменился - приложения больше не выглядят как монолит кода или один большой сервис, где весь функционал был в одном репозитории. Раньше сборки проектов занимали достаточно много времени, но с приходом контейнеров и таких методологий как DevOps приложения стали модульными, и теперь за каждую функцию или группу функций отвечают определенные сервисы этого приложения. Это можно сравнить с кирпичиками конструктора LEGO: из всех деталей складывается наше приложение, каждый сервис мы можем достать, чтобы что-то изменить и протестировать и вставить обратно в нашу конструкцию. Главная идея состоит в том, чтобы быстро внедрять новый функционал в уже имеющееся приложение. + +Но что если у нас таких сервисов тысячи, каждый за что-то отвечает и работает сам по себе? А если еще развернуты несколько реплик для отказоустойчивости? Как управлять всем и уделить внимание каждому из них? Как понять, что сервис правильно работает и взаимодействует с другими? Для этого и есть специальные системы, оркестраторы в своем роде, такие как HashiCorp Nomad, Docker Swarm и Kubernetes. + +Бизнес чаще всего ценит такие возможности К8S как собирать и тестировать только часть приложения, с которой мы работаем, что в разы уменьшает объем необходимых ресурсов; добавлять и убирать сервисы «на лету», тестировать новый функционал в разных регионах и смотреть, как он себя показывает. За счет этого Kubernetes, который дает унификацию и гибкость в способе обслуживания и содержания сервисов приложения. + +__Kubernetes предоставляет:__ + ++ Быструю и автоматическую масштабируемость. При росте нагрузки можно быстро добавить необходимые узлы приложения, а также быстро их вывести, чтобы не тратить драгоценные ресурсы ++ Гибкий подход к эксплуатации. Мы можем быстро и легко построить структуру приложения, так как вся структура описывается в конфигурационных файлах - манифестах ++ Гибкий подход в управлении. Kubernetes не потребует перестройки инфраструктуры и прочего, если вы захотели провести тестирование, внедрить новый сервис или сделать деплой по методологии blue-green ++ Универсальность. С помощью манифестов легко переехать, если вы захотели поменять провайдера или переезжали в свой собственный кластер ++ Низкий порог вхождения в использование. Kubernetes довольно легок в освоении манифестов, потому что большую часть работы он делает за вас + +Если вы задумываетесь о преимуществах, описанных выше, и можете точно сказать, что вам нужна гибкость в разработке и быстрое внедрение сервисов, адаптируемый подход и универсальность в управлении большим количеством сервисов и их реплик, то думаю, что пора попробовать Kubernetes и у себя. + +Однако, если ваш проект имеет постоянную нагрузку и не требует высокой степени гибкости и быстрого масштабирования, новый функционал появляется редко и у вас есть команда, уверенно работающая с существующим окружением, то на данный момент возможности K8S для бизнеса избыточны, но "посмотреть" на технологию в фоновом режиме все же стоит, так как те или иные условия могут и поменяться. + +[к оглавлению](#Docker) + +## Компоненты и архитектура Kubernetes + +Kubernetes, так как он был реализован как облачное решение, предпочтительнее разворачивать именно в облаке. + +Если вы решились ставить Kubernetes внутри, на своих собственных ресурсах, то вам придется позаботиться о развёртывании и обслуживании такой инфраструктуры вокруг кластера как: репозиторий для контейнеров, внешние или внутренние балансировщики, сетевые хранилища, хранилище секретов, решения для сбора логов и метрик. Также важна и внутренняя инфраструктура “кубера”: CertManager, Ingress, Istio и другие. + +1) Основной компонент K8S состоит из __Кластера (Cluster)__ + +Сам кластер состоит из __узлов или нодах (Nodes)__, в которых помимо контейнеров компонентов самого кластера, размещаются контейнеры наших проектов и сервисов. + +2) __Worker nodes__ - сервер, на котором запускаются и работают контейнеры + +Состоит из компонентов: + ++ kubelet - сервис или агент, который контролирует запуск компонентов (контейнеров) кластера ++ kube-proxy - конфигурирует правила сети на узлах + +3) Плоскость управления (__Master nodes__) управляет рабочими узлами и подами в кластере. Там располагаются компоненты, которые управляют узлами кластера и предоставляют доступ к API. + +__Control plane__ или Kubernete Master, состоит из компонентов: + ++ kube-apiserver - предоставляет API кубера ++ kube-scheduler - планирует размещение подов на узлах кластера ++ kube-controller-manager - запускает контроллеры ++ kubelet - сервис или агент, который контролирует запуск основных компонентов (контейнеров) кластер ++ etcd - распределенное key-value хранилище для всех данных кластера. Необязательно располагается внутри мастера, может стоять как отдельный кластер + +Когда запускаем команды управления они всегда посылаются именно на Master nodes. С воркер нодами напрямую не работаем. + +__Виды контроллеров__ ++ Deployments - контроллер, который управляет состоянием развертывания подов, которое описывается в манифесте, следит за удалением и созданием экземпляров подов. Управляет контроллерами ReplicaSet. ++ ReplicaSet - гарантирует, что определенное количество экземпляров подов всегда будет запущено в кластере. ++ StatefulSets - так же как и Deployments, управляет развертыванием и масштабированием набора подов, но сохраняет набор идентификаторов и состояние для каждого пода. ++ DaemonSet - гарантирует, что на каждом узле кластера будет присутствовать экземпляр пода. ++ Jobs - создает определенное количество подов и смотрит, пока они успешно не завершат работу. Если под завершился с ошибкой, повторяет создание, которое мы описали определенное количество раз. Если под успешно отработал, записывает это в свой журнал. ++ CronJob - запускает контроллеры Jobs по определенному расписанию. + +[к оглавлению](#Docker) + +## Kubernet Cluster + +Состоит минимум из одного (для непрерывной и безопасной работы может быть больше), Мастер нода. И минимум один Воркер нод (на практике их обычно больше) + +В мастер ноде указываем откуда брать Docker Images (из Dockerhub, AWS Container Registry ECR, Google Container Registry, Azure Container Registry и тд). Контейнеры разворачиваются на воркер нодах, заполняя память и процессоры. Если ресурсов будет не хватать, кубер может предложить масштабироваться, добавив еще воркер нодов. +```java +//minikube исп для управления при локальном запуске. при работе с AWS, Google кластером и тд нужно использовать соответствующий кластер и систему управления +minikube start -p NAME - запустить с нашим именем +minikube start --cpus=2 --memory=4gb/4000mb --disk-size=20gb - с конкретными ресурсами +minikube start --no-vtx-check - если ошибка и просить включить виртуализацию в биосе +minikube ssh - зайти внутрь виртуальной машины (можно через VirtualBox) + + +kubectl get componentstatuses - состояние кластера +kubectl cluster-info - инфа о кластере +kubectl get nodes +kubectl version --client + +``` + +Чтобы загрузить Докер образ в кластер нужно + +[к оглавлению](#Docker) + +## Kubectl +При работе с кластером нам потребуется инструмент командной строки kubectl. https://Kubernetes.io/ru/docs/tasks/tools/install-kubectl/ + +Его можно установить на локальную машину и управлять несколькими кластерами с одной точки. + +Основные команды, которые мы часто будем использовать: + ++ Kubectl get [имя объекта] -n [Имя_пространства_имен] - команда get, как несложно понять из ее названия, выводит нужную нам информацию в основном виде таблицы. Также с помощью нее можно получить yaml, который хранится внутри кластера. Очень часто применяется ключ -n для указания пространства имен, где лежат объекты. + +Примеры: +```java +kubectl get services -n Имя_неймспейса - выводит все сервисы в пространстве имён + +kubectl get pods --all-namespaces - выводит все поды во всех пространств имён + +kubectl get pods -o wide - выводит все поды в текущем пространстве имён с подробностями в виде расширенной таблицы + +kubectl get pods -n Имя_неймспейса - выводит все поды в пространстве имён +``` + ++ Kubectl apply -f [имя манифеста yaml] - команда apply применяет манифест к кластеру, управляет созданием объектов в кластере с помощью манифестов yaml. Если в манифесте не указано пространство имен, его можно задать с помощью ключа -n [Имя_пространства_имен]. + +Примеры: +```java +kubectl apply -f ./my-manifest.yaml - создать ресурсы + +kubectl apply -f ./my1.yaml -f ./my2.yaml - создать ресурсы из нескольких файлов + +kubectl apply -f ./dir - создать ресурсы из всех файлов манифеста в директории + +kubectl apply -f https://K8S.io/manifest - создать ресурсы из URL-адреса +``` + +Отличная шпаргалка по командам есть в документации https://Kubernetes.io/ru/docs/reference/kubectl/cheatsheet/ + +[к оглавлению](#Docker) + +## Основные Обьекты Kubernetes + +Контейнер не является обьектом K8S - это обьект Докера) + +1) Pod - самый маленький обьект Кубера, который мы можем создать. Состоит из контейнера (обычно) или нескольких контейнеров (если надо). Чаще один, чтобы при масштабировании не плодить лишние копии +2) Deployment - состоит из одного или нескольких одинаковых Подов, даже если в разных Нодах +3) Service - дает доступ к Подам, которые в Деплое, через ClusterIP, NodePort, LoadBalancer, ExternalName +4) Nodes - сервера, где все это работает +5) Cluster - обьединение нодов + +Т.е. расположены от меньше к большему и каждый следующий содержит предыдущие + +[к оглавлению](#Docker) + +## Kubernetes Pods + +1) Запуск и управление из консоли + +```java +kubectl run name --image=imageName --port=80 - создание пода с именем name из образа imageName (если нет локально скачает с Докерхаба) на порту 80 +kubectl get pods - список Подов +kubectl delete pods name - удалить Под с именем name +kubectl describe pods name - описание Пода name. Нода, на которой он развернут, его IP внутри кластера, контейнеры внутри Пода, Volums, последние Events и много другой инфы +kubectl exec name date - запустить команду в Поде, например date +kubectl exec -it name sh - запустить команду в Поде, например sh - консоль, и остаться внутри Пода +kubectl logs name - логи Пода name +kubectl port-forward name 7788:80 - переносим с локального порта 7788 на порт 80 Пода name +Ctrl+C - выйти из Пода +``` +2) Для запуска из YML файла + +Сам файл, например простой pod-my-web-ver1.yaml: +```java +apiVersion : v1 +kind: Pod +metadata: + name: my-web + labels: + env : prod + app : main + tier : fronted + owner : Shell26 +spec: + containers: + - name : container-apache + image: httpd:latest + ports: + - containerPort: 80 + + - name : container-api + image: tomcat:8.5.38 + ports: + - containerPort: 8080 +``` +И запускаем файл через консоль: +```java +kubectl apply -f ./pod-my-web-ver1.yaml +``` + +Если изменили файл, убиваем старый Под собранный из файла и запускаем новый: +```java +kubectl delete -f ./pod-my-web-ver1.yaml +kubectl apply -f ./pod-my-web-ver1.yaml +``` +__Без удаления можно менять только поле image, иначе ошибка. Достаточно повторно запустить kubectl apply__ + +[к оглавлению](#Docker) + +## Kubernetes Deployment + +Если что-то случится с Кластером и Нода умерла, то Кластер перезапустит Ноды, но Поды автоматически не пересоберутся. Для этого Поды создают через Девелопмент, а он управляет Подами + +1) Из консоли: +```java +kubectl create deployments name --image httpd:latest +kubectl get deploy +kubectl describe deployments name +kubectl scale deployment name --replicas 4 - создаст 4 копии и ReplicaSet, раскидав копии по разным Нодам +kubectl get rs - покажет ReplicaSet +kubectl autoscale deployment name --min=4 --max=6 --cpu-percent=80 - автоматически масштабирует для загрузки cpu на 80 и создаст обьект HorizontalPodAutoscaler +kubectl get hpa - покажет HorizontalPodAutoscaler +kubectl rollout status deployment/name +kubectl set image deployment/name oldimagename=newimagename - обновим в нашем Деплое контейнер oldimagename на новый newimagename +kubectl rollout undo deployment/name - откатит на прошлую версию +kubectl rollout history deployment/name - покажет историю обновлений +kubectl rollout undo deployment/name --to-revision=2 - откатит на версию 2 +kubectl rollout restart deployment/name - пересоберет текущую версию +``` + +2) Из файла-манифеста deployment-simple.yaml: + +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: my-web-deployment + labels: + app : my-k8s-application +spec: + replicas: 2 //сколько реплик делать на Нодах, если надо + selector: //с какими Подами будет работать Deployment + matchLabels: //с Подами которые совпадают по Lables + project: shell //именно такой + template: //тут часть с Подами + metadata: + labels: + project: shell + spec: + containers: + - name : shell-web + image: httpd:latest + ports: + - containerPort: 80 +... //можно в этом же файле разделим тремя точками, можно в отдельном файле +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: my-autoscaling +spec: + scaleTargetRef: //что он автоскейлит? + apiVersion: apps/v2 + kind: Deployment //Деплоймент + name: my-web-deployment //конкретно этот Деплоймент + minReplicas: 3 + maxReplacas: 5 + metrics: //как скейлит, основываясь на чем? + - type: Resource //либо такой вариант + resource: + name: cpu + target: + type: Utilization + averageUtilization: 90 + - type: Resource //либо такой вариант + resource: + name: memory + target: + type: Utilization + averageUtilization: 70 +``` + +Запускаем Манифест-файл: +```java +kubectl apply -f ./deployment-simple.yaml +``` +В итоге создаст два обьекта, Deployment и HorizontalPodAutoscaler + +[к оглавлению](#Docker) + +## Kubernetes Services +__Типы:__ + ++ ClusterIP - IP только внутри K8S Кластера. Выбирается по-умолчанию. Один такой сервис существует всегда, Kubernetes создает его автоматически ++ NodePort - Определенный порт на всех K8S Воркер Нодах ++ ExternalName - DNS CNAME Record ++ LoadBalancer - Только для облачных кластеров (AWS, Google, Azure) + +1) Из консоли: +```java +kubectl expose deployment name-deploy --type=NodePort --port 80 - создаем Сервис для управлением Деплоем name-deploy, типа NodePort на порту 80 + kubectl get services - + kubectl delete svc name +``` +2) Манифест-файл deployment-service.yaml: +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: my-web-deployment + labels: + app : my-k8s-application +spec: + replicas: 2 // + selector: // + matchLabels: // + project: shell //Сервис коннектится сюда, а не выше (Девелопмент) + template: // + metadata: + labels: + project: shell + spec: + containers: + - name : shell-web + image: httpd:latest + ports: + - containerPort: 80 +... +apiVersion: v1 +kind: Service +metadata: + name: my-service + labels: + env : prod + owner : Me +spec: + type: LoadBalancer + selector: //чем управляет сервис, не Деплоем, а какими Подами + project: shell //выбираем Поды с этим лейблом + ports: + - name : app-listener + protocol : TCP + port : 80 //Порт для LoadBalancer + targetPort: 80 //Порт для Pod +``` + +Запускаем Манифест-файл: +```java +kubectl apply -f ./deployment-service.yaml +``` + +[к оглавлению](#Docker) + +## Kubernetes Ingress Controller + +Отдельное приложение, которое позволяет управлять Сервисами внутри Кластера, с помощью одного публичного Сервиса, к которому есть публичный доступ. Вместо создания отдельных публичных Сервисов для каждого приложения внутри Кластера. Ingress Rules позволяют понять, к какому именно Ноду внутри Кластера обращается пользователь + +Было: +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker1.png) + +Стало: +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker2.png) + +Ingress Controller нужно подключать отдельно, например: +https://docs.google.com/spreadsheets/d/191WWNpjJ2za6-nbG4ZoUMXMpUK8KlCIosvQB0f-oq3k/edit#gid=907731238 + +Деплоим Ingress Controller к Кластеру +```java +kubectl apply -f https://projectcontour.io/quickstart/contour.yaml - адрес зависит от реализации Ingress Controller +kubectl get services -n projectcontour - посмотреть Ingress Controller +``` + +Например, у нас в кластере 3 Deployments: web1, web2 и tomcat. Web1 и web2 скалированы по 2 копии. Т.е. у нас висит 3 Деплоя, с 5 Подами (2+2+1) +```java +kubectl create deployment web1 --image=dockerhub/web1 +kubectl create deployment web2 --image=dockerhub/web1 +kubectl create deployment tomcat --image=tomcat:8.5.38 +kubectl get deploy + +kubectl scale deployment web1 --replicas 2 +kubectl scale deployment web2 --replicas 2 +kubectl get pods +``` + +Создадим 3 Сервиса, для каждого Деплоя. +```java +kubectl expose deployment web1 --port=80 +kubectl expose deployment web2 --port=80 +kubectl expose deployment tomcat --port=8080 +kubectl get services -o wide - чуть больше информации +``` +Нужно добавить Ingress Rules (ingress-rules.yaml): + +```java +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: ingtess-hosts + +spec: + rules: + - host: www.web1.shell.net + http: + paths: + - backend: + serviceName: web1 //на какой Сервис отправлять клиента, если запрос приходит на этот Хост + servicePort: 80 //на какой Порт отправлять клиента, если запрос приходит на этот Хост + + - host: www.shell.net //можно указать путь на страницу, вместо url + http: + paths: "/web2" + - backend: + serviceName: web2 + servicePort: 80 + + - host: cat.shell.net + http: + paths: + - backend: + serviceName: tomcat + servicePort: 8080 +``` +Запускаем Манифест-файл: +```java +kubectl apply -f ./ingress-rules.yaml +kubectl get ingress +kubectl describe ingress +``` + +[к оглавлению](#Docker) + +## Helm Charts + +https://helm.sh/ + +Если мы хотим что-то поменять в уже задеплоенном приложении, например образ, нужно отредактировать yaml файл, запустить команды на деплой Деплоймента и Сервиса. Если опять что-то поменяли, нужно все повторить. + +Helm Charts позволяет сделать поля в yaml файле динамическими, менять их из консоли или использовать несколько yaml файлов с разными параметрами(prod, test). Качаем файл с офф сайта в директорию, и запускаем из строки команду helm + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker3.png) + +```java +helm create Chart-Auto /автогенерация, много лишнего для начала +``` +Например возьму yaml файл с описанием Deployments и сделаю динамическими поля с названием проекта (берется из команды, при осздании Деплоимента) "image" и "replicaCount". Значения по-умолчанию хранятся в values.yaml + +deploymets.yaml : +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-deployment //.Release.Name берется из имени Деплоимента, когда создаем + labels: + app : {{ .Release.Name }}-application +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + project: {{ .Release.Name }} + template: + metadata: + labels: + project: {{ .Release.Name }} + spec: + containers: + - name : {{ .Release.Name }}-web + image: {{ .Values.container.image }} + ports: + - containerPort: 80 +``` +values.yaml : +```java +container: + image: dockerhub/web1 + +replicaCount: 2 +``` + +Запускаем Helm : +```java +helm install name1 path-to-Chart/ +helm list + +helm install name2 path-to-Chart/ --set container.image=dockerhub/web2 --set replicaCount=3 - перезапускаем меняя параметры +helm upgrade name2 path-to-Chart/ --set replicaCount=4 - обновляем +helm install name3 path-to-Chart/ -f prod_values.yaml - берем не дефолтные значения, а из другого yaml файла + +helm package path-to-Chart/ - упаковывает в файл path-to-Chart-0.1.0.tgz +helm install name4 -f path-to-Chart-0.1.0.tgz - запускаем из файла +``` + +Можно брать готовые Helm Charts из сети +```java +helm search repo - ищет в конкретном репозитории +helm repo add bitnami https://charts.bitname.com/bitnami - добавляем репо bitnami +helm install name123 bitnami/apache + +helm search hub apache - ищет в общем хабе +``` +[к оглавлению](#Docker) + diff --git a/examples/pom.xml b/examples/pom.xml index 6f1ab55..34ee800 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -26,7 +26,7 @@ false src/main/resources/google_checkstyle.xml - 4.12 + 4.13.1 2.5.0 1.7.22 diff --git a/examples/src/main/java/xyz/enhorse/example/Application.java b/examples/src/main/example/Application.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/Application.java rename to examples/src/main/example/Application.java diff --git a/examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java b/examples/src/main/example/java/concurrency/VolatileBehaviour.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java rename to examples/src/main/example/java/concurrency/VolatileBehaviour.java diff --git a/examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java b/examples/src/main/example/java/concurrency/WaitNotify.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java rename to examples/src/main/example/java/concurrency/WaitNotify.java diff --git a/examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java b/examples/src/main/example/oop/PolymorphicTicTock.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java rename to examples/src/main/example/oop/PolymorphicTicTock.java diff --git a/img/Conc3.png b/img/Conc3.png new file mode 100644 index 0000000..944c1c6 Binary files /dev/null and b/img/Conc3.png differ diff --git a/img/Conc4.png b/img/Conc4.png new file mode 100644 index 0000000..098b0a2 Binary files /dev/null and b/img/Conc4.png differ diff --git a/img/Conc5.png b/img/Conc5.png new file mode 100644 index 0000000..9e91242 Binary files /dev/null and b/img/Conc5.png differ diff --git a/img/Conc6.png b/img/Conc6.png new file mode 100644 index 0000000..ad7a955 Binary files /dev/null and b/img/Conc6.png differ diff --git a/img/Conc7.png b/img/Conc7.png new file mode 100644 index 0000000..84881c0 Binary files /dev/null and b/img/Conc7.png differ diff --git a/img/Conc8.png b/img/Conc8.png new file mode 100644 index 0000000..f359dc7 Binary files /dev/null and b/img/Conc8.png differ diff --git a/img/Conc9.png b/img/Conc9.png new file mode 100644 index 0000000..1fa295c Binary files /dev/null and b/img/Conc9.png differ diff --git a/img/Concurrency1.jpg b/img/Concurrency1.jpg new file mode 100644 index 0000000..5e11afc Binary files /dev/null and b/img/Concurrency1.jpg differ diff --git a/img/Concurrency2.png b/img/Concurrency2.png new file mode 100644 index 0000000..2708913 Binary files /dev/null and b/img/Concurrency2.png differ diff --git a/img/Core1.jpg b/img/Core1.jpg new file mode 100644 index 0000000..c71c874 Binary files /dev/null and b/img/Core1.jpg differ diff --git a/img/Core2.png b/img/Core2.png new file mode 100644 index 0000000..87d7642 Binary files /dev/null and b/img/Core2.png differ diff --git a/img/Core3.png b/img/Core3.png new file mode 100644 index 0000000..477b6f6 Binary files /dev/null and b/img/Core3.png differ diff --git a/img/Core4.png b/img/Core4.png new file mode 100644 index 0000000..770e846 Binary files /dev/null and b/img/Core4.png differ diff --git a/img/Docker1.jpg b/img/Docker1.jpg new file mode 100644 index 0000000..ccf55b2 Binary files /dev/null and b/img/Docker1.jpg differ diff --git a/img/Hibernate1.png b/img/Hibernate1.png new file mode 100644 index 0000000..99d49e3 Binary files /dev/null and b/img/Hibernate1.png differ diff --git a/img/Hibernate2.png b/img/Hibernate2.png new file mode 100644 index 0000000..5adf426 Binary files /dev/null and b/img/Hibernate2.png differ diff --git a/img/Hibernate3.png b/img/Hibernate3.png new file mode 100644 index 0000000..5720b98 Binary files /dev/null and b/img/Hibernate3.png differ diff --git a/img/Hibernate4.png b/img/Hibernate4.png new file mode 100644 index 0000000..4864deb Binary files /dev/null and b/img/Hibernate4.png differ diff --git a/img/Hibernate5.png b/img/Hibernate5.png new file mode 100644 index 0000000..0f85859 Binary files /dev/null and b/img/Hibernate5.png differ diff --git a/img/Hibernate6.png b/img/Hibernate6.png new file mode 100644 index 0000000..43c9993 Binary files /dev/null and b/img/Hibernate6.png differ diff --git a/img/Hibernate7.png b/img/Hibernate7.png new file mode 100644 index 0000000..1295617 Binary files /dev/null and b/img/Hibernate7.png differ diff --git a/img/Hibernate8.png b/img/Hibernate8.png new file mode 100644 index 0000000..ca07f64 Binary files /dev/null and b/img/Hibernate8.png differ diff --git a/img/OOP1.png b/img/OOP1.png new file mode 100644 index 0000000..d60db3e Binary files /dev/null and b/img/OOP1.png differ diff --git a/img/OOP10.png b/img/OOP10.png new file mode 100644 index 0000000..8b57597 Binary files /dev/null and b/img/OOP10.png differ diff --git a/img/OOP11.png b/img/OOP11.png new file mode 100644 index 0000000..dff4433 Binary files /dev/null and b/img/OOP11.png differ diff --git a/img/OOP12.png b/img/OOP12.png new file mode 100644 index 0000000..6b0bdbd Binary files /dev/null and b/img/OOP12.png differ diff --git a/img/OOP13.png b/img/OOP13.png new file mode 100644 index 0000000..b6374f1 Binary files /dev/null and b/img/OOP13.png differ diff --git a/img/OOP14.png b/img/OOP14.png new file mode 100644 index 0000000..82cb7ed Binary files /dev/null and b/img/OOP14.png differ diff --git a/img/OOP2.png b/img/OOP2.png new file mode 100644 index 0000000..5fff135 Binary files /dev/null and b/img/OOP2.png differ diff --git a/img/OOP3.png b/img/OOP3.png new file mode 100644 index 0000000..15ea0e2 Binary files /dev/null and b/img/OOP3.png differ diff --git a/img/OOP4.png b/img/OOP4.png new file mode 100644 index 0000000..3682fd0 Binary files /dev/null and b/img/OOP4.png differ diff --git a/img/OOP5.png b/img/OOP5.png new file mode 100644 index 0000000..da79d40 Binary files /dev/null and b/img/OOP5.png differ diff --git a/img/OOP6.png b/img/OOP6.png new file mode 100644 index 0000000..1d09350 Binary files /dev/null and b/img/OOP6.png differ diff --git a/img/OOP7.png b/img/OOP7.png new file mode 100644 index 0000000..fea0fee Binary files /dev/null and b/img/OOP7.png differ diff --git a/img/OOP8.png b/img/OOP8.png new file mode 100644 index 0000000..8323f48 Binary files /dev/null and b/img/OOP8.png differ diff --git a/img/OOP9.png b/img/OOP9.png new file mode 100644 index 0000000..7f616c0 Binary files /dev/null and b/img/OOP9.png differ diff --git a/img/SQL1.png b/img/SQL1.png new file mode 100644 index 0000000..2fed639 Binary files /dev/null and b/img/SQL1.png differ diff --git a/img/Spring1.png b/img/Spring1.png new file mode 100644 index 0000000..755a22a Binary files /dev/null and b/img/Spring1.png differ diff --git a/img/Spring2.png b/img/Spring2.png new file mode 100644 index 0000000..44d8a24 Binary files /dev/null and b/img/Spring2.png differ diff --git a/img/Spring3.png b/img/Spring3.png new file mode 100644 index 0000000..3d7db15 Binary files /dev/null and b/img/Spring3.png differ diff --git a/img/Spring4.png b/img/Spring4.png new file mode 100644 index 0000000..2d0a3ad Binary files /dev/null and b/img/Spring4.png differ diff --git a/img/Spring5.png b/img/Spring5.png new file mode 100644 index 0000000..fd2ba3c Binary files /dev/null and b/img/Spring5.png differ diff --git a/img/Spring6.png b/img/Spring6.png new file mode 100644 index 0000000..d5aaec2 Binary files /dev/null and b/img/Spring6.png differ diff --git a/img/Spring7.png b/img/Spring7.png new file mode 100644 index 0000000..2ad1d53 Binary files /dev/null and b/img/Spring7.png differ diff --git a/img/Spring8.png b/img/Spring8.png new file mode 100644 index 0000000..753da16 Binary files /dev/null and b/img/Spring8.png differ diff --git a/img/Spring9.png b/img/Spring9.png new file mode 100644 index 0000000..300acef Binary files /dev/null and b/img/Spring9.png differ diff --git a/img/Test1.png b/img/Test1.png new file mode 100644 index 0000000..d9da1e5 Binary files /dev/null and b/img/Test1.png differ diff --git a/img/docker1.png b/img/docker1.png new file mode 100644 index 0000000..67dd84b Binary files /dev/null and b/img/docker1.png differ diff --git a/img/docker2.png b/img/docker2.png new file mode 100644 index 0000000..efad929 Binary files /dev/null and b/img/docker2.png differ diff --git a/img/docker3.png b/img/docker3.png new file mode 100644 index 0000000..4a9c95e Binary files /dev/null and b/img/docker3.png differ diff --git a/io.md b/io.md index af4b3fd..773b7ff 100644 --- a/io.md +++ b/io.md @@ -2,6 +2,7 @@ # Потоки ввода/вывода в Java + [В чём заключается разница между IO и NIO?](#В-чём-заключается-разница-между-io-и-nio) ++ [Какие классы поддерживают чтение и запись потоков в компрессированном формате?](#Какие-классы-поддерживают-чтение-и-запись-потоков-в-компрессированном-формате) + [Какие особенности NIO вы знаете?](#Какие-особенности-nio-вы-знаете) + [Что такое _«каналы»_?](#Что-такое-каналы) + [Какие существуют виды потоков ввода/вывода?](#Какие-существуют-виды-потоков-вводавывода) @@ -39,6 +40,18 @@ [к оглавлению](#Потоки-вводавывода-в-java) +## Какие классы поддерживают чтение и запись потоков в компрессированном формате? ++ DeflaterOutputStream - компрессия данных в формате deflate. ++ Deflater - компрессия данных в формат ZLIB ++ ZipOutputStream - потомок DeflaterOutputStream для компрессии данных в формат Zip. ++ GZIPOutputStream - потомок DeflaterOutputStream для компрессии данных в формат GZIP. ++ InflaterInputStream - декомпрессия данных в формате deflate. ++ Inflater - декомпрессия данных в формате ZLIB ++ ZipInputStream - потомок InflaterInputStream для декомпрессии данных в формате Zip. ++ GZIPInputStream - потомок InflaterInputStream для декомпрессии данных в формате GZIP. + +[к оглавлению](#Потоки-вводавывода-в-java) + ## Какие особенности NIO вы знаете? + __Каналы и селекторы__: NIO поддерживает различные типы каналов. Канал является абстракцией объектов более низкого уровня файловой системы (например, отображенные в памяти файлы и блокировки файлов), что позволяет передавать данные с более высокой скоростью. Каналы не блокируются и поэтому Java предоставляет еще такие инструменты, как селектор, который позволяет выбрать готовый канал для передачи данных, и сокет, который является инструментом для блокировки. + __Буферы__: имеет буферизация для всех классов-обёрток примитивов (кроме Boolean). Появился абстрактный класс Buffer, который предоставляет такие операции, как clear, flip, mark и т.д. Его подклассы предоставляют методы для получения и установки данных. diff --git a/java8.md b/java8.md index fa05093..2b0c2e2 100644 --- a/java8.md +++ b/java8.md @@ -143,7 +143,7 @@ public static void main(String[] args) { Printable printer = s -> System.out.println(s); printer.print("Hello, world"); } -```java +``` + _Блочные лямбда-выражения_ обрамляются фигурными скобками. В блочных лямбда-выражениях можно использовать внутренние вложенные блоки, циклы, конструкции `if`, `switch`, создавать переменные и т.д. Если блочное лямбда-выражение должно возвращать значение, то явным образом применяется оператор `return`: @@ -225,6 +225,8 @@ public static void main(String[] args) { Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод. +https://www.examclouds.com/ru/java/java-core-russian/method-references-russian + [к оглавлению](#java-8) ## Какие виды ссылок на методы вы знаете? diff --git a/java9-16.md b/java9-16.md new file mode 100644 index 0000000..7fff5f3 --- /dev/null +++ b/java9-16.md @@ -0,0 +1,39 @@ +[Вопросы для собеседования](README.md) + +# Java 9-16 ++ [Java 9](java9-16.md#Java-9) ++ [Java 10](java9-16.md#Java-10) ++ [Java 11](java9-16.md#Java-11) ++ [Java 12](java9-16.md#Java-12) ++ [Java 13](java9-16.md#Java-13) ++ [Java 14](java9-16.md#Java-14) ++ [Java 15](java9-16.md#Java-15) ++ [Java 16](java9-16.md#Java-16) + +# Java 9 + ++ Полная поддержка клиента HTTP 2.0: Вопрос в скорости, и HTTP 2.0 предоставляет более высокие результаты, колеблющиеся от 11.81% до 47.7% по сравнении с клиентом HTTP 1.1 + ++ Проект Jigsaw (в переводе “головоломка”) направлен на модуляризацию Java. Это значит, что программный код разбивается на части и организуется по модулям в зависимости от задач, которые эти модули выполняют. Это позволяет использовать модули повторно, упрощает их организацию и отладку. Что ведет к оптимизированной и отлаженной разработке ПО. Это ключевое отличие Java 9 от Java 8. + ++ Jshell: Новый инструмент командной строки. Если разработчик хочет автономно запустить несколько строк Java, то это можно выполнить без необходимости заворачивать все в отдельный метод или проект. Это интерактивный инструмент, позволяющий тестировать небольшие части кода без необходимости создавать новые классы. Он оснащен функциями истории и автозаполнения, а также рядом других особенностей, включая сохранение и загрузку написанных выражений. + Что касается точек с запятой — можно забыть про них: Существуют различные альтернативы наподобие плагинов REPL для популярных IDE или веб-консоли Java REPL, но ни одна из них не является официальной. + ++ Microbenchmark: Теперь производительность отдельных небольших частей кода можно измерить стандартизированным методом. Анализ JMH за наносекунды уникален для Java 9 + ++ G1 — дефолтный сборщик мусора. Очень часто сталкиваемся с заблуждением, что в Java есть только один сборщик мусора, хотя по факту их 4. Parallel / Throughput Collector считался дефолтным в прошлых версиях, но теперь его заменил G1, который был представлен в Java 7 и был разработан для лучшей поддержки куч размером более 4GB. Он вызывает меньше GC пауз, но если они все же происходят, то длятся дольше. + ++ Изображения с мульти-разрешением. Этот API позволяет инкапсулировать набор изображений с разными разрешениями в единый объект. Таким образом, разработчик может получить изображение с определенным разрешением или все варианты внутри одного. +# Java 10 + +# Java 11 + +# Java 12 + +# Java 13 + +# Java 14 + +# Java 15 + +# Java 16 \ No newline at end of file diff --git a/jcf.md b/jcf.md index da4312d..cf5f0c9 100644 --- a/jcf.md +++ b/jcf.md @@ -24,6 +24,7 @@ + [Чем отличается `ArrayList` от `Vector`?](#Чем-отличается-arraylist-от-vector) + [Зачем добавили `ArrayList`, если уже был `Vector`?](#Зачем-добавили-arraylist-если-уже-был-vector) + [Чем отличается `ArrayList` от `LinkedList`? В каких случаях лучше использовать первый, а в каких второй?](#Чем-отличается-arraylist-от-linkedlist-В-каких-случаях-лучше-использовать-первый-а-в-каких-второй) ++ [Как устроен `HashMap`?](#Как-устроен-hashmap) + [Что работает быстрее `ArrayList` или `LinkedList`?](#Что-работает-быстрее-arraylist-или-linkedlist) + [Какое худшее время работы метода `contains()` для элемента, который есть в `LinkedList`?](#Какое-худшее-время-работы-метода-contains-для-элемента-который-есть-в-linkedlist) + [Какое худшее время работы метода `contains()` для элемента, который есть в `ArrayList`?](#Какое-худшее-время-работы-метода-contains-для-элемента-который-есть-в-arraylist) @@ -52,7 +53,6 @@ + [В `WeakHashMap` используются WeakReferences. А почему бы не создать `PhantomHashMap` на PhantomReferences?](#В-weakhashmap-используются-weakreferences-А-почему-бы-не-создать-phantomhashmap-на-phantomreferences) + [`LinkedHashMap` - что в нем от `LinkedList`, а что от `HashMap`?](#linkedhashmap---что-в-нем-от-linkedlist-а-что-от-hashmap) + [В чем проявляется «сортированность» `SortedMap`, кроме того, что `toString()` выводит все элементы по порядку?](#В-чем-проявляется-сортированность-sortedmap-кроме-того-что-tostring-выводит-все-элементы-по-порядку) -+ [Как устроен `HashMap`?](#Как-устроен-hashmap) + [Согласно Кнуту и Кормену существует две основных реализации хэш-таблицы: на основе открытой адресации и на основе метода цепочек. Как реализована `HashMap`? Почему, по вашему мнению, была выбрана именно эта реализация? В чем плюсы и минусы каждого подхода?](#Согласно-Кнуту-и-Кормену-существует-две-основных-реализации-хэш-таблицы-на-основе-открытой-адресации-и-на-основе-метода-цепочек-Как-реализована-hashmap-Почему-по-вашему-мнению-была-выбрана-именно-эта-реализация-В-чем-плюсы-и-минусы-каждого-подхода) + [Как работает `HashMap` при попытке сохранить в него два элемента по ключам с одинаковым `hashCode()`, но для которых `equals() == false`?](#Как-работает-hashmap-при-попытке-сохранить-в-него-два-элемента-по-ключам-с-одинаковым-hashcode-но-для-которых-equals--false) + [Какое начальное количество корзин в `HashMap`?](#Какое-начальное-количество-корзин-в-hashmap) @@ -98,32 +98,38 @@ _«Коллекция»_ - это структура данных, набор к ## Назовите основные интерфейсы JCF и их реализации. На вершине иерархии в Java Collection Framework располагаются 2 интерфейса: `Collection` и `Map`. Эти интерфейсы разделяют все коллекции, входящие во фреймворк на две части по типу хранения данных: простые последовательные наборы элементов и наборы пар «ключ — значение» соответственно. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Core4.png) + Интерфейс `Collection` расширяют интерфейсы: + `List` (список) представляет собой коллекцию, в которой допустимы дублирующие значения. Элементы такой коллекции пронумерованы, начиная от нуля, к ним можно обратиться по индексу. Реализации: - - `ArrayList` - инкапсулирует в себе обычный массив, длина которого автоматически увеличивается при добавлении новых элементов. - - `LinkedList` (двунаправленный связный список) - состоит из узлов, каждый из которых содержит как собственно данные, так и две ссылки на следующий и предыдущий узел. - - `Vector` — реализация динамического массива объектов, методы которой синхронизированы. - - `Stack` — реализация стека LIFO (last-in-first-out). наследуется от `Vector` + - `ArrayList` - инкапсулирует в себе обычный массив, длина которого автоматически увеличивается при добавлении новых элементов. Позволяет хранить любые данные, включая null в качестве элемента. + - `LinkedList` (двунаправленный связный список) - состоит из узлов, каждый из которых содержит как собственно данные, так и две ссылки на следующий и предыдущий узел. Позволяет хранить любые данные, включая null. + - `Vector` — реализация динамического массива объектов, методы которой синхронизированы. Позволяет хранить любые данные, включая null в качестве элемента. Синхронизирован, но как и Hashtable, эту коллекцию не рекомендуется использовать, если не требуется достижения потокобезопасности. + - `Stack` — реализация стека LIFO (last-in-first-out). Является частично синхронизированной коллекцией (кроме метода добавления push() + `Set` (сет) описывает неупорядоченную коллекцию, не содержащую повторяющихся элементов. Реализации: - - `HashSet` - использует HashMap для хранения данных. В качестве ключа и значения используется добавляемый элемент. Из-за особенностей реализации порядок элементов не гарантируется при добавлении. - - `LinkedHashSet` — гарантирует, что порядок элементов при обходе коллекции будет идентичен порядку добавления элементов. -+ `SortedSet` — расширяющий интерфейс `Set`, описывает упорядоченное множество, отсортированное в возрастающем порядке или по порядку, заданному реализацией интерфейса `Comparator`. + - `HashSet` - использует HashMap для хранения данных. В качестве ключа используется добавляемый элемент, а в качестве значения — объект-пустышка new Object(). Из-за особенностей реализации порядок элементов не гарантируется при добавлении. + - `LinkedHashSet` — гарантирует, что порядок элементов при обходе коллекции будет идентичен порядку добавления элементов. + - `SortedSet` — расширяющий интерфейс `Set`, описывает упорядоченное множество, отсортированное в возрастающем порядке или по порядку, заданному реализацией интерфейса `Comparator`. - `TreeSet` — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + `Queue` — (очередь) предназначена для хранения элементов с предопределённым способом вставки и извлечения FIFO (first-in-first-out): - + `PriorityQueue` — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». -+ `Deque` — расширяет вышеописанный интерфейс `Queue` и определяет поведение двунаправленной очереди, которая работает как обычная однонаправленная очередь, либо как стек, действующий по принципу LIFO (последний вошел - первый вышел). - + `ArrayDeque` — реализация интерфейса `Deque`, который расширяет интерфейс `Queue` методами, позволяющими реализовать конструкцию вида LIFO (last-in-first-out). + - `PriorityQueue` — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + - интерфейс `Deque` — расширяет вышеописанный интерфейс `Queue` и определяет поведение двунаправленной очереди, которая работает как обычная однонаправленная очередь, либо как стек, действующий по принципу LIFO (последний вошел - первый вышел). + - `ArrayDeque` — реализация интерфейса `Deque`, который расширяет интерфейс `Queue` методами, позволяющими реализовать конструкцию вида LIFO (last-in-first-out). Интерфейс `Map` реализован классами: - -+ `Hashtable` — хэш-таблица, методы которой синхронизированы. Не позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. -+ `HashMap` — хэш-таблица. Позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. -+ `LinkedHashMap` — упорядоченная реализация хэш-таблицы. -+ `TreeMap` — реализация основанная на красно-чёрных деревьях. Является упорядоченной и предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + `WeakHashMap` — реализация хэш-таблицы, которая организована с использованием _weak references_ для ключей (сборщик мусора автоматически удалит элемент из коллекции при следующей сборке мусора, если на ключ этого элемента нет жёстких ссылок). ++ `Hashtable` — хэш-таблица, методы которой синхронизированы. Не позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. ++ `HashMap` — хэш-таблица. Не синхронизирована, позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. + - `LinkedHashMap` — упорядоченная реализация хэш-таблицы, благодаря двунаправленным связям между элементами (аналогично LinkedList) ++ интерфейс `SortesMap` + - интерфейс `NavigableMap` + - `TreeMap` — реализация основанная на красно-чёрных деревьях. Является упорядоченной и предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Core3.png) [к оглавлению](#java-collections-framework) @@ -707,11 +713,6 @@ PhantomReference при вызове метода `get()` возвращает [к оглавлению](#java-collections-framework) -## Как устроен `HashMap`? -`HashMap` состоит из «корзин» (bucket). С технической точки зрения «корзины» — это элементы массива, которые хранят ссылки на списки элементов. При добавлении новой пары «ключ-значение», вычисляет хэш-код ключа, на основании которого вычисляется номер корзины (номер ячейки массива), в которую попадет новый элемент. Если корзина пустая, то в нее сохраняется ссылка на вновь добавляемый элемент, если же там уже есть элемент, то происходит последовательный переход по ссылкам между элементами в цепочке, в поисках последнего элемента, от которого и ставится ссылка на вновь добавленный элемент. Если в списке был найден элемент с таким же ключом, то он заменяется. - -[к оглавлению](#java-collections-framework) - ## Согласно Кнуту и Кормену существует две основных реализации хэш-таблицы: на основе открытой адресации и на основе метода цепочек. Как реализована `HashMap`? Почему, по вашему мнению, была выбрана именно эта реализация? В чем плюсы и минусы каждого подхода? `HashMap` реализован с использованием метода цепочек, т.е. каждой ячейке массива (корзине) соответствует свой связный список и при возникновении коллизии осуществляется добавление нового элемента в этот список. @@ -779,7 +780,6 @@ PhantomReference при вызове метода `get()` возвращает [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода get(key) для ключа, которого нет в `HashMap`? -## Какое худшее время работы метода get(key) для ключа, который есть в `HashMap`? ___O(N)___. Худший случай - это поиск ключа в `HashMap`, вырожденного в список по причине совпадения ключей по `hashCode()` и для выяснения хранится ли элемент с определённым ключом может потребоваться перебор всего списка. [к оглавлению](#java-collections-framework) diff --git a/jdbc.md b/jdbc.md index 121dbca..b2fa5ce 100644 --- a/jdbc.md +++ b/jdbc.md @@ -8,6 +8,7 @@ + [Что такое JPA?](#Что-такое-jpa) + [Различия между JPA JDBC и Hibernate?](#Различия-между-jpa-jdbc-и-hibernate) + [Что такое JPQL и HQL?](#Что-такое-jpql-и-hql) ++ [Criteria API](#-Criteria-API) + [В чем заключаются преимущества использования JDBC?](#В-чем-заключаются-преимущества-использования-jdbc) + [В чем заключаются преимущества использования Hibernate?](#В-чем-заключаются-преимущества-использования-hibernate) + [Что из себя представляет JDBC URL?](#Что-из-себя-представляет-jdbc-url) @@ -16,6 +17,7 @@ + [Опишите основные этапы работы с базой данных с использованием JDBC.](#Опишите-основные-этапы-работы-с-базой-данных-при-использовании-jdbc) + [Как зарегистрировать драйвер JDBC?](#Как-зарегистрировать-драйвер-jdbc) + [Как установить соединение с базой данных?](#Как-установить-соединение-с-базой-данных) ++ [Транзакции в Hibernate.](#Транзакции-в-Hibernate) + [Какие уровни изоляции транзакций поддерживаются в JDBC?](#Какие-уровни-изоляции-транзакций-поддерживаются-в-jdbc) + [При помощи чего формируются запросы к базе данных?](#При-помощи-чего-формируются-запросы-к-базе-данных) + [Чем отличается Statement от PreparedStatement?](#Чем-отличается-statement-от-preparedstatement) @@ -29,6 +31,8 @@ + [Что такое встраиваемый (Embeddable) класс?](#Что-такое-встраиваемый-(embeddable)-класс) + [Может ли встраиваемый (Embeddable) класс содержать другой встраиваемый (Embeddable) класс?](#Может-ли-встраиваемый-(embeddable)-класс-содержать-другой-встраиваемый-(embeddable)-класс) + [Какие требования JPA устанавливает к встраиваемым (Embeddable) классам?](#Какие-требования-jpa-устанавливает-к-встраиваемым-(embeddable)-классам) ++ [Что такое Mapped Superclass?](#Что-такое-mapped-superclass) ++ [Mapped Superclass vs. Embeddable class](#Mapped-Superclass-vs.-Embeddable-class) + [Основные классы и интерфейсы JPA?](#Основные-классы-и-интерфейсы-jpa) + [Что такое Метамодель?](#Что-такое-метамодель) + [Основные классы и интерфейсы Hibernate?](#Основные-классы-и-интерфейсы-hibernate) @@ -39,12 +43,16 @@ + [Аннотации JPA?](#Аннотации-jpa) + [Аннотации Hibernate?](#Аннотации-hibernate) + [Кеширование в Hibernate?](#Кеширование-в-hibernate) ++ [Для чего нужна аннотация @Cacheable?](#Для-чего-нужна-аннотация-@Cacheable) + [Стратегии кеширования?](#Стратегии-кеширования) -+ [Что такое Mapped Superclass?](#Что-такое-mapped-superclass) + [Какие три типа стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA?](#Какие-три-типа-стратегии-наследования-мапинга-(inheritance-mapping-strategies)-описаны-в-jpa) + [Стратегии загрузки объектов в Hibernate?](#Стратегии-загрузки-объектов-в-hibernat) + [Для чего нужна аннотация Basic?](#Для-чего-нужна-аннотация-basic) ++ [Для чего нужна аннотация Column?](#Для-чего-нужна-аннотация-Column) + [Для чего нужна аннотация Access?](#Для-чего-нужна-аннотация-access) ++ [Для чего нужны аннотации @Embedded и @Embeddable?](#Для-чего-нужны-аннотации-@Embedded-и-@Embeddable) ++ [Для чего нужны аннотации @JoinColumn, @JoinColumns и @JoinTable?](#Для-чего-нужны-аннотации-@JoinColumn-@JoinColumns-и-@JoinTable) ++ [Для чего нужны аннотации @OrderBy и @OrderColumn](#Для-чего-нужны-аннотации-@OrderBy-и-@OrderColumn) + [Для чего нужны callback методы в JPA? К каким сущностям применяются аннотации callback методов? Перечислите семь callback методов (или, что тоже самое, аннотаций callback методов)](#Для-чего-нужны-callback-методы-в-jpa) + [Какие видов блокировок (lock) описаны в спецификации JPA?](#Какие-видов-блокировок-(lock)-описаны-в-спецификации-jpa) + [Как можно изменить настройки fetch?](#Как-можно-изменить-настройки-fetch) @@ -53,16 +61,18 @@ + [Как определить владельца связи?](#Как-определить-владельца-связи) + [Что происходит с таблицами при изпользовании mappedBy?](#Что-происходит-с-таблицами-при-изпользовании-mappedby) + [FetchType стратегии по-умолчанию?](#fetchtype-стратегии-по-умолчанию) -+ [Data и Enum значения в БД?](#data-и-enum-значения-в-БД) ++ [Enum значения в БД?](#enum-значения-в-БД) ++ [Как мапятся даты (до Java 8 и после)?](#Как-мапятся-даты-(до-Java-8-и-после)) + [PersistenceContext](#persistencecontext) + [Entity graph](#Entity-graph) + [Пробелма n+1 select и как ее решить?](#Пробелма-n+1-select-и-как-ее-решить) + [JOIN FETCH vs Join](#join-fetch-vs-join) ++ [Criteria Api](#Criteria-Api) + [OrderBy vs OrderColumn](#orderby-vs-ordercolumn) + [Как создать составной ключ](#Как-создать-составной-ключ) + [GeneratedValue](#generatedvalue) + [OrphanRemoval vs cascadeType.remove](#Orphanremoval-vs-cascadeType.remove) -+ [ElementCollection](#Elementcollection) ++ [ElementCollection - Как сохранять в БД коллекции базовых типов](#Elementcollection-Как-сохранять-в-БД-коллекции-базовых-типов) ## Что такое _ORM_? @@ -93,8 +103,139 @@ Hibernate одна из самых популярных открытых реа ## Что такое JPQL и HQL? + JPQL — Java Persistence Query Language. Фактически это как SQL, только запросы делаются не к таблицам, а к классам. JPQL основан на HQL. + +В отличии от SQL в запросах JPQL есть автоматический __полиморфизм__, то есть каждый запрос к Entity возвращает не только объекты этого Entity, но также объекты всех его классов-потомков, независимо от стратегии наследования (например, запрос select * from Animal, вернет не только объекты Animal, но и объекты классов Cat и Dog, которые унаследованы от Animal). Чтобы исключить такое поведение используется функция TYPE в where условии (например select * from Animal a where TYPE(a) IN(Animal, Cat) уже не вернет объекты класса Dog). + +Запросы, как обычные, так и именованные, формируются из EntityManager: +```java +Query query = entityManager.createQuery( +"select p " + +"from Person p " + +"where p.name like :name" +); +TypedQuery typedQuery = entityManager.createQuery( +"select p " + +"from Person p " + +"where p.name like :name", Person.class +); +@NamedQuery( +name = "get_person_by_name", +query = "select p from Person p where name = :name" +) +Query query = entityManager.createNamedQuery( "get_person_by_name" ); +TypedQuery typedQuery = entityManager.createNamedQuery( +"get_person_by_name", Person.class +); +``` + + HQL — Hibernate Query Language. Аналог SQL, но работает с сохраняемыми объектами (Persistent Objects) и их полями (аттрибутами класса). Мы также имеем возможность испольщовать обычные SQL – запросы в Hibernate используя Native SQL +В Hibernate HQL-запрос представлен org.hibernate.query.Query, полученный из Session. Если HQL является именованным запросом, то будет использоваться Session#getNamedQuery, в противном случае требуется Session#createQuery. HQL пердоставляет дополнительные возможности по сравнению с JPQL. +```java +org.hibernate.query.Query query = session.createQuery( + "select p " + + "from Person p " + + "where p.name like :name" +); +org.hibernate.query.Query query = session.getNamedQuery( "get_person_by_name" ); +``` + +## Criteria API +__Начиная с версии 5.0 собственный Hibernate Criteria API признан устаревшим и не развивается. Вместо него рекомендуется использовать JPA Criteria API. + +Начиная с версии 5.2 Hibernate Criteria API объявлен deprecated и не рекомендуется к использованию.__ + +__Hibernate Criteria API__ + +Это тоже язык запросов, аналогичный JPQL (Java Persistence query language), однако запросы основаны на методах и объектах. Hibernate Criteria API является более объектно-ориентированным для запросов, которые получают результат из базы данных. Для операций update, delete или других DDL манипуляций использовать Criteria API нельзя. Критерии используются только для выборки из базы данных в более объектно-ориентированном стиле. Используется для динамических запросов. Запросы выглядят так: +```java +session.createCriteria(Person.class) +.setMaxResults(10) +.list() +.forEach(System.out::println); +``` + +Запрос выше полностью аналогичен запросу HQL "from Person". С Criteria также работают и все те вещи, которые работают и с Query: пейджинг, таймауты и т.д. + +Разумеется, в Criteria запросах можно и нужно накладывать условия, по которым объекты будут отбираться: +```java +session.createCriteria(Person.class) +.add(Restrictions.eq("lastName", "Testoff")) +.list() +.forEach(System.out::println); +``` + +__JPA Criteria API__ + +Criteria API - это актуальный API, используемый для определения запросов для сущностей. Это альтернативный способ определения JPQL-запроса. Эти запросы типобезопасны, переносимы и легко меняются путем изменения синтаксиса. + +Основные преимущества JPA Criteria API: +- ошибки могут быть обнаружены во время компиляции; +- позволяет динамически формировать запросы на этапе выполнения приложения. + +Запросы на основе строк JPQL и запросы на основе критериев JPA одинаковы по производительности и эффективности. + +Для простых статических запросов предпочтительнее использовать строковые запросы JPQL (например, в виде именованных запросов). Для динамических запросов, которые создаются во время выполнения - JPA Criteria API может быть предпочтительней. Например, построение динамического запроса на основе полей, которые пользователь заполняет в рантайме в форме, которая содержит много необязательных полей. Ожидается, что построение этого запроса будет более ясным и понятным при использовании JPA Criteria API, поскольку устраняет необходимость в создании запроса с использованием многих операций конкатенации строк. + +Пример использования JPA Criteria API: +```java +EntityManager em = entityManagerFactory.createEntityManager(); +em.getTransaction().begin(); +CriteriaBuilder cb = em.getCriteriaBuilder(); +CriteriaQuery personCriteria = cb.createQuery(Person.class); +Root personRoot = personCriteria.from(Person.class); +personCriteria.select(personRoot); +em.createQuery(personCriteria) +.getResultList() +.forEach(System.out::println); +``` + +Алгоритм: +- создаём EntityManager, открываем транзакцию и создаём CriteriaBuilder, который будет строить объекты запросов. С помощью CriteriaBuilder создаём CriteriaQuery, который параметризуется типом, который этот запрос возвращает. +- Затем создаём корневой объект, от которого производится обход дерева свойств при накладывании ограничений или указании, что выбирать. +- Последним шагом говорится, что же мы хотим выбрать и, наконец, запрос отправляется в EntityManager, где и выполняется как обычно. + +Построенный выше весьма многословный пример эквивалентен JPQL запросу «from Person». + +Все шаги, перечисленные выше, являются обязательными для создания запроса с помощью Criteria API. Важно понимать, что корневой объект указывает JPQL, откуда будут браться данные, а CriteriaQuery указывает тип возвращаемых данных. И типы Root и CriteriaQuery могут отличаться: +```java +CriteriaQuery passportCriteria = cb.createQuery(Passport.class); +Root personPassportRoot = passportCriteria.from(Person.class); +passportCriteria.select(personPassportRoot.get("passport")); +em.createQuery(passportCriteria) +.getResultList() +.forEach(System.out::println); +``` + +Этот запрос аналогичен JPQL запросу «select passport from Person» и показывает, что класс, из которого запрашиваются данные и класс, который вернёт запрос, могут быть разными. + +__Metamodel и типобезопасность__ + +Все примеры выше решают проблему с программным созданием запросов, но всё ещё бессильны перед изменениями сущностей. В самом деле, изменив в сущности Person поле workingPlaces на jobs - развалится этот запрос: +```java +CriteriaQuery personWorkCriteria = cb.createQuery(Person.class); +Root personWorkRoot = personWorkCriteria.from(Person.class); +Join company = personWorkRoot.join("workingPlaces"); +personWorkCriteria.select(personWorkRoot); +personWorkCriteria.where(cb.equal(company.get("name"), "Acme Ltd")); +em.createQuery(personWorkCriteria) +.getResultList() +.forEach(System.out::println); +``` + +И мы не узнаем, что он развалится, пока не попробуем его исполнить. Metamodel решает эту проблему, создавая специальные описательные классы, которые используются в Criteria API вместо имён полей. Сам Metamodel класс выглядит примерно вот так: +```java +@StaticMetamodel(Company.class) +public abstract class Company_ extends AbstractIdentifiableObject_ { +public static volatile SingularAttribute name; +public static volatile CollectionAttribute workers; +} +``` + +В Metamodel классе описываются, какие поля присутствуют в сущности, какого они типа, коллекция это или нет и т.д. Для каждой сущности создаётся свой класс Metаmodel. + +Создаются классы Metamodel разумеется не вручную. То есть можно их и вручную создать, но тогда пропадает автоматичность проверки и теряется смысл всей этой затеи. Обычно же классы Metamodel генерируются на этапе компиляции тем или иным методом. Конкретная реализация генерации зависит от конкретной реализации JPA и может меняться + ## В чем заключаются преимущества использования JDBC? Преимуществами JDBC считают: @@ -245,6 +386,19 @@ static Connection getConnection(String url, String user, String password) [к оглавлению](#jdbc) +## Транзакции в Hibernate. +Hibernate построен поверх JDBC API и реализует модель транзакций JDBC. Если быть точным, Hibernate способен работать или с JDBC транзакциями или с JTA транзакциями — Java Transaction API. +Транзакцию можно начать вызовом beginTransaction() объекта Session, либо запросить у Session связанный с ней объект Transaction и позвать у последнего метод begin(). С объектом Session всегда связан ровно один объект Transaction, доступ к которому может быть получен вызовом getTransaction(). +Методов для подтверждения или отката транзакции у объекта Session нет, необходимо всегда обращаться к объекту Transaction. +В отличие от JDBC в Hibernate не поддерживаются Savepoints и транзакция может только быть подтверждена или откачена, без промежуточных вариантов. + +Операции над транзакциями +У объекта Transaction есть ещё несколько методов, кроме commit() и rollback(), которые позволяют тонко управлять поведением транзакции. Метод isActive() позволяет проверить, есть ли в рамках объекта Transaction управляемая им транзакция. Очевидно, что такая транзакция существует в промежутке времени между вызовами begin() и commit()/rollback(). +Метод setRollbackOnly() помечает транзакцию как откаченную в будущем. В отличие от rollback() этот метод не закрывает транзакцию и все последующие запросы к базе будут продолжать выполняться в рамках той же самой транзакции, но завершить эту транзакцию можно будет только откатом и вызовом rollback(). Вызов commit() на такой транзакции выбросит исключение. Проверить состояние транзакции можно вызовом getRollbackOnly(). + +https://easyjava.ru/data/hibernate/tranzakcii-i-blokirovki-v-hibernate/ + + ## Какие уровни изоляции транзакций поддерживаются в JDBC? __Уровень изолированности транзакций__ — значение, определяющее уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность данных, но при этом может снижаться количество параллельно выполняемых транзакций. С другой стороны, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных. @@ -373,7 +527,9 @@ public vois runStoredProcedure(final Connection connection) throws Exception { ## Что такое Entity? Entity это легковесный хранимый объект бизнес логики (persistent domain object). Основная программная сущность это entity класс, который так же может использовать дополнительные классы, который могут использоваться как вспомогательные классы или для сохранения состояния еntity. -JPA указывает что она может работать как с свойствами классов (property), оформленные в стиле JavaBeans (с геттерами и сеттерами), либо с полями (field), то есть переменными класса (instance variables). Соответственно, при этом тип доступа будет либо property access или field access. Оба типа элементов Entity класса называются _атрибутами Entity класса_. +При этом у одной и той же табличке в БД может быть несколько Entity. + +Через аннотации можно указать, как работать со свойствами классов (property). Через методы, когда аннотации стоят над методами (геттерами и сеттерами), либо когда аннотации стоят над полями (через Reflection), то есть переменными класса (instance variables). Соответственно, при этом тип доступа будет либо property access или field access. Оба типа элементов Entity класса называются _атрибутами Entity класса_. Допустимые типы атрибутов у Entity классов: + примитивные типы и их обертки Java, @@ -384,7 +540,7 @@ JPA указывает что она может работать как с св + embeddable классы + и коллекции типов 1-6 -Требования JPA к Entity классам: +__Требования JPA к Entity классам:__ 1) Entity класс должен быть отмечен аннотацией Entity или описан в XML файле конфигурации JPA, 2) Entity класс должен содержать public или protected конструктор без аргументов (он также может иметь конструкторы с аргументами), 3) Entity класс должен быть классом верхнего уровня (top-level class), @@ -395,10 +551,51 @@ JPA указывает что она может работать как с св 8) Поля Entity класс должны быть напрямую доступны только методам самого Entity класса и не должны быть напрямую доступны другим классам, использующим этот Entity. Такие классы должны обращаться только к методам (getter/setter методам или другим методам бизнес-логики в Entity классе), 9) Enity класс должен содержать первичный ключ, то есть атрибут или группу атрибутов которые уникально определяют запись этого Enity класса в базе данных, + +__Требования Hibernate к Entity классам:__ +1) Класс сущности должен иметь конструктор без аргументов, который может быть не только public или protected, но и package visibility (default). +2) Класс сущности не обязательно должен быть классом верхнего уровня. +3) Технически Hibernate может сохранять финальные классы или классы с финальными методами (getter / setter). Однако, как правило, это не очень хорошая идея, так как это лишит Hibernate возможности генерировать прокси для отложенной загрузки сущности. +4) Hibernate не запрещает разработчику приложения открывать прямой доступ к переменным экземпляра и ссылаться на них извне класса сущности. Однако обоснованность такого подхода спорна. + +[к оглавлению](#jdbc) + +## Может ли абстрактный класс быть Entity? +Абстрактный класс может быть Entity классом. Абстрактный Entity класс +отличается от обычных Entity классов только тем, что нельзя создать объект этого +класса. Имена абстрактных классов могут использоваться в запросах. + +Абстрактные Entity классы используются в наследовании, когда их потомки +наследуют поля абстрактного класса: + +```java +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class Employee { + @Id + @GeneratedValue + private long id; + private String name; + ............. +} +@Entity +@Table(name = "FULL_TIME_EMP") +public class FullTimeEmployee extends Employee { + private int salary; + ............. +} +@Entity +@Table(name = "PART_TIME_EMP") +public class PartTimeEmployee extends Employee { + private int hourlyRate; + ............. +} +``` + [к оглавлению](#jdbc) ## Как наследуется Entity? -+ Может наследоваться и от других Entity классов, и от не Entity классов. ++ Может наследоваться и от других Entity классов, и от не Entity классов. Состояние (поля) не Entity суперкласса не является персистентным, то есть не хранится в БД и не обрабатывается провайдером (Hibernate), поэтому любое такое состояние (поля), унаследованное Entity классом, также не будет отображаться в БД. Не Entity суперклассы не могут участвовать в операциях EntityManager или Query. Любые маппинги или аннотации отношений в не Entity суперклассах игнорируются. + Не Entity классы так же могут наследоваться от Entity. + Может быть абстрактным, при этом он сохраняет все свойства Entity, за исключением того что его нельзя непосредственно инициализировать. @@ -421,7 +618,76 @@ Plain Old Java Object - простой Java-объект, не унаследо [к оглавлению](#jdbc) ## Что такое встраиваемый (Embeddable) класс? -Встраиваемый (Embeddable) класс это класс который не используется сам по себе, только как часть одного или нескольких Entity классов. Entity класс могут содержать как одиночные встраиваемые классы, так и коллекции таких классов. Также такие классы могут быть использованы как ключи или значения map. Во время выполнения каждый встраиваемый класс принадлежит только одному объекту Entity класса и не может быть использован для передачи данных между объектами Entity классов (то есть такой класс не является общей структурой данных для разных объектов). В целом, такой класс служит для того чтобы выносить определение общих атрибутов для нескольких Entity, можно считать что JPA просто встраивает в Entity вместо объекта такого класса те атрибуты, которые он содержит. +Встраиваемый (Embeddable) класс это класс который не используется сам по себе, только как часть одного или нескольких Entity классов. + +Например, у нас может быть встраиваемый класс ClassA, который представляет собой композицию строкового и числового значений, и эти два поля будут добавлены в класс EntityA: +```java +@Entity +public class EntityA { + @Id + @GeneratedValue + private int id; + @Embedded + private ClassA classARef; + ............. +} +@Embeddable +public class ClassA { + private String myStr; + private int myInt; + ............. +} +``` +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate2.png) + + +Entity класс могут содержать как одиночные встраиваемые классы, так и коллекции таких классов. Также такие классы могут быть использованы как ключи или значения map. Во время выполнения каждый встраиваемый класс принадлежит только одному объекту Entity класса и не может быть использован для передачи данных между объектами Entity классов (то есть такой класс не является общей структурой данных для разных объектов). В целом, такой класс служит для того чтобы выносить определение общих атрибутов для нескольких Entity, можно считать что JPA просто встраивает в Entity вместо объекта такого класса те атрибуты, которые он содержит. +То есть, если класс Person с полями name и age встроен и в класс Driver, и в класс Baker, то у обоих последних классов появятся оба поля из класса Person. Но если у объекта Driver эти поля будут иметь значения “Иван” и “35”, то эти же поля у объекта Baker могут иметь совершенно иные значения, никак не связанные с объектом Driver. + +__Особенности встраиваемых классов__ +1) все поля встраиваемого класса, даже коллекции, станут полями класса, в +который происходит встраивание; +2) встраиваемые классы могут быть встроены в одну и ту же сущность несколько +раз, нужно только поменять имена полей; +3) экземпляры встраиваемых классов, в отличие от экземпляров сущностей, не +имеют собственного персистентного состояния, вместо этого они существуют +только как часть состояния объекта, которому они принадлежат; +4) встраиваемые классы могут использовать в качестве полей: +➢ базовые типы; +➢ коллекции базовых типов (с аннотацией @ElementCollection); +➢ другие встраиваемые классы; +➢ коллекции других встраиваемых классов (с аннотацией +@ElementCollection); +➢ сущности; +➢ коллекции сущностей; +5) сущность может использовать в качестве полей одиночные встраиваемые +классы и коллекции встраиваемых классов; +6) встраиваемые классы могут использоваться в качестве ключей и значений Map. + +Так как мы можем встраивать классы в неограниченное количество других классов, то у каждого класса, содержащего встраиваемый класс, мы можем изменить названия полей из встраиваемого класса. Например, у класса Driver поля из встраиваемого класса Person будут изменены с name на driver_name и с age на driver_age + +```java +@Embeddable +public class Person { + private String name; + private int age; +} +@Entity +public class Driver { + @Embedded + @AttributeOverrides({ + @AttributeOverride( name = "name", + column = @Column(name = "driver_name")), + @AttributeOverride( name = "age", + column = @Column(name = "driver_age")) + }) + private Person person; + ... +} +``` +Сущности, которые имеют встраиваемые классы, могут аннотировать поле или свойство аннотацией @Embedded, но не обязаны это делать. + +Можно использовать для денормализации БД (ускорений запросов к БД) [к оглавлению](#jdbc) @@ -439,7 +705,61 @@ Plain Old Java Object - простой Java-объект, не унаследо 1. Такие классы должны удовлетворять тем же правилам что Entity классы, за исключением того что они не обязаны содержать первичный ключ и быть отмечены аннотацией Entity (см. вопрос 10), 2. Embeddable класс должен быть отмечен аннотацией Embeddable или описан в XML файле конфигурации JPA. +## Что такое Mapped Superclass? +Mapped Superclass это класс от которого наследуются Entity, он может содержать аннотации JPA, однако сам такой класс не является Entity, ему не обязательно выполнять все требования установленные для Entity (например, он может не содержать первичного ключа). Такой класс не может использоваться в операциях EntityManager или Query. Такой класс должен быть отмечен аннотацией MappedSuperclass или соответственно описан в xml файле. + +__Особенности__ +‒ Должен быть помечен аннотацией @MappedSuperclass или описан в xml файле. +‒ Не может использоваться в операциях EntityManager или Query, вместо этого нужно использовать классы-наследники. +‒ Не может состоять в отношениях с другими сущностями (в сущности нельзя создать поле с типом сопоставленного суперкласса). +‒ Может быть абстрактным. +‒ Не имеет своей таблицы в БД. + +Для того, чтобы использовать Mapped Superclass, достаточно унаследовать его в классах-потомках: +```java +@MappedSuperclass +public class Employee { + @Id + @GeneratedValue + private long id; + private String name; + ............. +} +@Entity +@Table(name = "FULL_TIME_EMP") +public class FullTimeEmployee extends Employee { + private int salary; + ............. +} +@Entity +@Table(name = "PART_TIME_EMP") +public class PartTimeEmployee extends Employee { + private int hourlyRate; + ............. +} +``` +В указанном примере кода в БД будут таблицы FULLTIMEEMPLOYEE и PARTTIMEEMPLOYEE, но таблицы EMPLOYEE не будет: + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate3.png) + +Это похоже на стратегию наследования “Таблица для каждого конкретного класса сущностей”, но в модели данных нет объединения таблиц или наследования. Также тут нет таблицы для Mapped Superclass. Наследование существует только в объектной модели. + +Основным недостатком использования сопоставленного суперкласса является то, что полиморфные запросы невозможны, то есть мы не можем загрузить всех наследников Mapped Superclass. + + +## Mapped Superclass vs. Embeddable class +__Сходства:__ +1) не являются сущностями и могут иметь все аннотации, кроме @Entity; +2) не имеют своих таблиц в БД; +3) не могут использоваться в операциях EntityManager или Query. + +__Различия:__ +1) MappedSuperclass - наследование, Embeddable class - композиция (экземпляр «части» может входить только в одно целое (или никуда не входить)); +2) поля из Mapped Superclass могут быть у сущности в одном экземпляре, полей из Embeddable class может быть сколько угодно (встроив в сущность Embeddable class несколько раз и поменяв имена полей); +3) в сущности нельзя создать поле с типом сопоставленного суперкласса, а с Embeddable можно и нужно. + [к оглавлению](#jdbc) + ## Основные классы и интерфейсы JPA __EntityManagerFactory__ – фабричный класс EntityManager. Он создает и управляет несколькими экземплярами EntityManager. Создание EntityManagerFactory довольно дорогая операция, поэтому обычно её создают один раз и на всё приложение. @@ -463,15 +783,19 @@ __Методы операций над Entity:__ + refresh (обновление данных) + detach (удаление из управление JPA) + lock (блокирование Enity от изменений в других thread). + __Методы получение данных:__ + find (поиск и получение Entity) + createQuery, createNamedQuery, createNativeQuery + contains + createNamedStoredProcedureQuery, createStoredProcedureQuery + __Получение других сущностей JPA:__ + getTransaction, getEntityManagerFactory, getCriteriaBuilder, getMetamodel, getDelegate + __Работа с EntityGraph:__ + createEntityGraph, getEntityGraph + __Общие операции над EntityManager или всеми Entities:__ + close, isOpen, getProperties, setProperty, clear. @@ -512,10 +836,40 @@ __Query__ - Этот объект использует HQL или SQL для ч [к оглавлению](#jdbc) ## Способы сконфигурировать Hibernate? ++ используя аннотации + hibernate.cfg.xml + hibernate.properties + persistence.xml +Самый частый способ конфигурации: через аннотации и файл persistence.xml, что касается файлов hibernate.properties и hibernate.cfg.xml, то hibernate.cfg.xml главнее (если в приложение есть оба файла, то принимаются настройки из файла hibernate.cfg.xml). Конфигурация аннотациями, хоть и удобна, но не всегда возможна, например, если для разных баз данных или для разных ситуаций вы хотите иметь разные конфигурацию сущностей, то следует использовать xml файлы конфигураций. +По мимо этого хибернейт можно сконфигурировать с использованием SessionFactory или EntityManagerFactory. +При использовании JPA или Hibernate у вас есть два варианта: +Вы можете загрузиться с помощью встроенного механизма Hibernate и создать SessionFactory. +Или вы можете создать JPA EntityManagerFactory +Начальная загрузка через JPA должна быть предпочтительной. Кроме того, если вы использовали JPA, и вы вводили EntityManagerFactory через @PersistenceUnit аннотации: + +```java +@PersistenceUnit +private EntityManagerFactory entityManagerFactory; +``` +Вы можете легко получить доступ к базовому SessionFactory используя unwrap метод: +SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); +То же самое можно сделать с JPA EntityManager. Если вы вводите EntityManager через @PersistenceContext аннотацию: + +```java +@PersistenceContext +private EntityManager entityManager; +``` +Вы можете легко получить доступ к базовому, Session используя unwrap метод: +```java +Session session = entityManager.unwrap(Session.class); +``` + +Таким образом, вам следует загружать через JPA, использовать EntityManagerFactory и EntityManager и развертывать их только в связанных с ними интерфейсах Hibernate, когда вы хотите получить доступ к некоторым специфичным для Hibernate методам, которые недоступны в JPA, например, к извлечению объекта через его естественный идентификатор. + +https://javarush.ru/groups/posts/1502-voprosih-na-sobesedovanie-hibernate +https://stackoverflow.com/questions/5640778/hibernate-sessionfactory-vs-entitymanagerfactory + ## SessionFactory vs EntityManagerFactory и Session vs EntityManager? Hibernate появился раньше JPA. К этому времени у HIBERNATE был большой наработанный функционал, а в для первой версии JPA удалось согласовать только часть этого объема функционала. Поэтому, разработчики HIBERNATE сознательно пошли на то, чтобы в HIBERNATE имелось два пути работы - старый путь - нативный HIBERNATE (через интерфейс Session) и новый путь JPA (через интерфейс EntityManager). @@ -533,6 +887,8 @@ Hibernate появился раньше JPA. К этому времени у HIB + detached — объект был создан, но не управляется (или больше не управляется) JPA. Объект, который до этого был привязан к контексту персистентности, но теперь отделен от него. Отделение могло произойти по двум причинам: контекст персистентности был закрыт (закончилась тарзакция, закрылась сессия), либо объект был явно отделен методом detach или clear от ещё существующего контекста. Подобный объект имеет заполненное поле id, сгенерированное базой, но контекст персистентности больше не следит за изменением этого объекта. Что бы снова присоединить такой объект к контексту персистентности, и перевести его снова в Persistent надо вызвать метод merge, который сравнит состояние текущего detached объекта и данные из базы + removed — объект создан, управляется JPA, но будет удален после commit'a транзакции. Объект, ассоциированная с которым запись была удалена из базы. Такой объект так же не отслеживается контекстом персистентности и информация о нем больше не хранится в кэше первого уровня. Такой объект можно только сохранить в базу снова методом persist +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate1.png) + ```java DETACHED . | @@ -557,30 +913,30 @@ NEW ———— persist(entity) —————> MANAGED <————— cr ## Влияние операций EntityManager на Entity объекты различный жизненных циклов? __Persist__
-1) Если статус Entity new, то он меняется на managed и объект будет сохранен в базу при commit'е транзакции или в результате flush операций, -2) Если статус уже managed, операция игнорируется, однако зависимые Entity могут поменять статус на managed, если у них есть аннотации каскадных изменений, -3) Если статус removed, то он меняется на managed, -4) Если статус detached, будет выкинут exception сразу или на этапе commit'а транзакции, +1) New -> меняется на managed и объект будет сохранен в базу при commit'е транзакции или в результате flush операций, +2) Managed -> операция игнорируется, однако зависимые Entity могут поменять статус на managed, если у них есть аннотации каскадных изменений, +3) Removed -> то он меняется на managed, +4) Detached -> будет выкинут exception сразу или на этапе commit'а транзакции, __Remove__
-1) Если статус Entity new, операция игнорируется, однако зависимые Entity могут поменять статус на removed, если у них есть аннотации каскадных изменений и они имели статус managed, -2) Если статус managed, то статус меняется на removed и запись объект в базе данных будет удалена при commit'е транзакции (так же произойдут операции remove для всех каскадно зависимых объектов), -3) Если статус removed, то операция игнорируется, -4) Если статус detached, будет выкинут exception сразу или на этапе commit'а транзакции, +1) New -> операция игнорируется, однако зависимые Entity могут поменять статус на removed, если у них есть аннотации каскадных изменений и они имели статус managed, +2) Managed -> статус меняется на removed и запись объект в базе данных будет удалена при commit'е транзакции (так же произойдут операции remove для всех каскадно зависимых объектов), +3) Removed -> операция игнорируется, +4) Detached -> будет выкинут exception сразу или на этапе commit'а транзакции, __Merge__
-1) Если статус detached, то либо данные будет скопированы в существующей managed entity с тем же первичным ключом, либо создан новый managed в который скопируются данные, -1) Если статус Entity new, то будет создана новый managed entity, в который будут скопированы данные прошлого объекта, -2) Если статус managed, операция игнорируется, однако операция merge сработает на каскадно зависимые Entity, если их статус не managed, -3) Если статус removed, будет выкинут exception сразу или на этапе commit'а транзакции, +1) Detached -> то либо данные будет скопированы в существующей managed entity с тем же первичным ключом, либо создан новый managed в который скопируются данные, +1) New -> будет создана новый managed entity, в который будут скопированы данные прошлого объекта, +2) Managed -> операция игнорируется, однако операция merge сработает на каскадно зависимые Entity, если их статус не managed, +3) Removed -> будет выкинут exception сразу или на этапе commit'а транзакции, __Refresh__
-1) Если статус Entity managed, то в результате операции будут востановлены все изменения из базы данных данного Entity, так же произойдет refresh всех каскадно зависимых объектов, +1) Managed -> в результате операции будут востановлены все изменения из базы данных данного Entity, так же произойдет refresh всех каскадно зависимых объектов, 2) Если статус new, removed или detached, будет выкинут exception, __Detach__
-1) Если статус Entity managed или removed, то в результате операции статус Entity (и всех каскадно-зависимых объектов) станет detached. -2) Если статус new или detached, то операция игнорируется, +1) Managed или Removed -> в результате операции статус Entity (и всех каскадно-зависимых объектов) станет detached. +2) New или Detached -> операция игнорируется, ## Аннотации JPA @Access — аннотация используется для указания типа доступа связанного класса сущности, сопоставленного супер класса или встраиваемого класса или атрибута сущности. @@ -953,42 +1309,250 @@ DIRTY - Неявный оптимистический механизм блок ## Кеширование в Hibernate? __Кеш 1-го уровня__ — кеш первого уровня всегда привязан к объекту сессии. Hibernate всегда по умолчанию использует этот кеш и его нельзя отключить. При использовании методов save(), update(), saveOrUpdate(), load(), get(), list(), iterate(), scroll() всегда будет задействован кеш первого уровня. +При работе с БД можно использовать HQL, Criteria API или Native Query. При этом при использовании нативных запросов, они не кешируются. Даже кешем первого уровня, который не отключается. + Интересно поведение кэша первого уровня при использовании ленивой загрузки. При загрузке объекта методом load() или объекта с лениво загружаемыми полями, лениво загружаемые данные в кэш не попадут. При обращении к данным будет выполнен запрос в базу и данные будут загружены и в объект и в кэш. А вот следующая попытка лениво загрузить объект приведёт к тому, что объект сразу вернут из кэша и уже полностью загруженным. ++ Кэш первого уровня связан с объектом Session, а другие объекты сеанса в приложении его не видят. ++ Область действия объектов кэша имеет сессию. Как только сессия закрыта, кэшированные объекты исчезают навсегда. ++ Кэш первого уровня включен по умолчанию, и вы не можете его отключить. ++ Когда мы запрашиваем объект в первый раз, он извлекается из базы данных и сохраняется в кэше первого уровня, связанном с сессией хибернейта. ++ Если мы снова запросим тот же объект с тем же объектом сеанса, он будет загружен из кэша, и никакой SQL-запрос не будет выполнен. ++ Загруженный объект можно удалить из сеанса с помощью метода evict(). Следующая загрузка этого объекта снова вызовет базу данных, если она была удалена с помощью метода evict(). ++ Весь кэш сеанса можно удалить с помощью метода clear(). Это удалит все сущности, хранящиеся в кэше. ++ Кэш первого уровня не является потокобезопасным. ++ Кэш первого уровня привязан к сессии и уничтожается следом за уничтожением сессии. + +Из этого следует один важный вывод: кэш первого уровня не является средством оптимизации большого количества повторяющихся запросов на выборку со стороны клиента, т.к. каждый запрос будет обрабатываться в отдельной транзакции, на которую будет выделен новый объект entityManager, который связан напрямую с новой сессией. Соответственно, на 20 одинаковых запросов пользователя будет создано 20 entityManager и 20 сессий. Будет выделено 20 транзакций, даже если запросы обрабатываются и поступают одновременно. +Кэш первого уровня нужен: + 1. Для сохранения целостности данных + 2. Оптимизации запросов на изменение/удаление + 3. Оптимизация запросов на выборку в рамках одной транзакции +В пределах жизненного цикла одной сессии и в рамках одной транзакции мы можем изменить внутреннее состояние сущности неограниченное количество раз, каждое изменение будет вноситься в кэш первого уровня. Но в базу запрос отправится только тогда, когда будет сделан комит транзакции. В базу отправятся те данные, которые содержит сущность на момент последнего изменения. До тех пор, пока транзакция не будет закончена - все изменения будут храниться в кэше. Даже если мы вызовем 20 раз метод setField() у любой сущности - в базу в итоге отправится только один запрос. +Если же мы вынуждены читать в рамках одной транзакции несколько раз одни и те же данные, то, единожды загрузив данные запросом из базы мы будем в дальнейшем работать с данными внутри кэша, не повторяя дополнительных запросов. Например, если достать List и затем достать конкретного юзера с id=2, то запрос в базу не будет произведен, т.к. список всех пользователей уже лежит в кэше. Так же, если мы, уже после того как достали пользователя с id=2 изменили 10 раз его имя, а затем снова выберем список всех пользователей - мы и в этом случае не получим дополнительных запросов. В описанном выше случае будет произведено только два запроса: на выборку списка всех пользователей в самом начала и один запрос на изменение состояния пользователя уже в конце транзакции. + +evict() используется для удаления конкретного объекта из кэша, связанного с сеансом, а метод clear() используется для удаления всех кэшированных объектов, связанных с сеансом. + __Кеш 2-го уровня__ — кеш второго уровня привязан к объекту-фабрике сессий (Session Factory object). Что как бы подразумевает, что видимость этого кеша гораздо шире кеша первого уровня. Чтение из кеша второго уровня происходит только в том случае, если нужный объект не был найден в кеше первого уровня. По умолчанию кеш второго уровня отключен. Для включения необходимо добавить следующие строки в Вашем конфигурационном файле JPA (persistence.xml): -`` +Будут ли в нашем приложении кэшироваться сущности и связанные с ними состояния, определяется значением элемента shared-cache-mode файла persistence.xml (или в свойстве javax.persistence.sharedCache.mode конфигурационного файла). Если в файле для элемента shared-cache-mode установлено значение: -`//или в более старых версиях` +- ENABLE_SELECTIVE (дефолтное и рекомендуемое значение): только сущности с аннотацией @Cacheable (равносильно значению по умолчанию @Cacheable(value=true)) будут сохраняться в кэше второго уровня. +- DISABLE_SELECTIVE: все сущности будут сохраняться в кэше второго уровня, за исключением сущностей, помеченных аннотацией @Cacheable(value=false)как некэшируемые. +- ALL: сущности всегда кэшируются, даже если они помечены как некэшируемые. +- NONE: ни одна сущность не кэшируется, даже если помечена как кэшируемая. При данной опции имеет смысл вообще отключить кэш второго уровня. +- UNSPECIFIED: применяются значения по умолчанию для кэша второго уровня, определенные Hibernate. Это эквивалентно тому, что вообще не используется shared-cache-mode, так как Hibernate не включает кэш второго уровня, если используется режим UNSPECIFIED. -`//` +На самом деле, хибернейт сам не реализует кеширование как таковое. А лишь предоставляет структуру для его реализации, поэтому подключить можно любую реализацию, которая соответствует спецификации нашего ORM фреймворка. Из популярных реализаций можна выделить следующие: EHCache, OSCache, SwarmCache. -`` +Для Hibernate требуется только реализация интерфейса org.hibernate.cache.spi.RegionFactory, который инкапсулирует все детали, относящиеся к конкретным провайдерам. По сути, RegionFactory действует как мост между Hibernate и поставщиками кэша. В примерах будем использовать Ehcache. Что нужно сделать: -На самом деле, хибернейт сам не реализует кеширование как таковое. А лишь предоставляет структуру для его реализации, поэтому подключить можно любую реализацию, которая соответствует спецификации нашего ORM фреймворка. Из популярных реализаций можна выделить следующие: EHCache, OSCache, SwarmCache. +- добавить мавен-зависимость кэш-провайдера нужной версии: +```java + +org.hibernate +hibernate-ehcache +5.2.2.Final + +``` -@Cacheable это аннотация JPA и позволяет объекту быть закэшированным. Hibernate поддерживает эту аннотацию в том же ключе. -@Cache это аннотация Hibernate, настраивающая тонкости кэширования объекта в кэше второго уровня Hibernate. Аннотации @Cacheable достаточно, чтобы объект начал кэшироваться с настройками по умолчанию. При этом @Cache использованная без @Cacheable не разрешит кэширование объекта. +- включить кэш второго уровня и определить конкретного провайдера: +```java +hibernate.cache.use_second_level_cache=true +hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory +``` -@Cache принимает три параметра: -+ include, имеющий по умолчанию значение all и означающий кэширование всего объекта. Второе возможное значение, non-lazy, запрещает кэширование лениво загружаемых объектов. Кэш первого уровня не обращает внимания на эту директиву и всегда кэширует лениво загружаемые объекты. -+ region позволяет задать имя региона кэша для хранения сущности. Регион можно представить как разные кэши или разные части кэша, имеющие разные настройки на уровне реализации кэша. Например, я мог бы создать в конфигурации ehcache два региона, один с краткосрочным хранением объектов, другой с долгосрочным и отправлять часто изменяющиеся объекты в первый регион, а все остальные во второй. -+ usage задаёт стратегию одновременного доступа к объектам. +- установить у нужных сущностей JPA-аннотацию @Cacheable, обозначающую, что сущность нужно кэшировать, и Hibernate-аннотацию @Cache, настраивающую детали кэширования, у которой в качестве параметра указать стратегию параллельного доступа (о которой говорится далее), например так: +```java +@Entity +@Table(name = "shared_doc") +@Cacheable +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +public class SharedDoc{ +private Set users; +} +``` -Помимо всего этого, вероятней всего, Вам также понадобится отдельно настроить и саму реализацию кеша. В случае с EHCache это нужно сделать в файле ehcache.xml. Ну и в завершение еще нужно указать самому хибернейту, что именно кешировать. К счастью, это очень легко можно сделать с помощью аннотаций, например так: @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +- не обязательно устанавливать у сущностей JPA-аннотацию @Cacheable, если работаем с Hibernate напрямую, не через JPA. -Еще одна важная деталь про кеш второго уровня про которую стоило бы упомянуть — хибернейт не хранит сами объекты Ваших классов. Он хранит информацию в виде массивов строк, чисел и т. д. И идентификатор объекта выступает указателем на эту информацию. Концептуально это нечто вроде Map, в которой id объекта — ключ, а массивы данных — значение. Приблизительно можно представить себе это так: +- чтобы кэш не “съел” всю доступную память, можно, например, ограничивать количество каждого типа сущностей, хранимых в кэше: +```java + + + +``` -`1 -> { "Pupkin", 1, null , {1,2,5} }` +__Стратегия параллельного доступа к объектам__ -При этом зависимости Вашего класса по умолчанию также не кешируются. Они будут подгружаться из БД, если их отдельно ен закешировать. +Проблема заключается в том, что кэш второго уровня доступен из нескольких сессий сразу и несколько потоков программы могут одновременно в разных транзакциях работать с одним и тем же объектом. Следовательно надо как-то обеспечивать их одинаковым представлением этого объекта. В Hibernate существует четыре стратегии одновременного доступа к объектам в кэше: +- READ_ONLY: Используется только для сущностей, которые никогда не изменяются (будет выброшено исключение, если попытаться обновить такую сущность). Очень просто и производительно. Подходит для некоторых статических данных, которые не меняются. +- +- NONSTRICT_READ_WRITE: Кэш обновляется после совершения транзакции, которая изменила данные в БД и закоммитила их. Таким образом, строгая согласованность не гарантируется, и существует небольшое временное окно между обновлением данных в БД и обновлением тех же данных в кэше, во время которого параллельная транзакция может получить из кэша устаревшие данные. + +- READ_WRITE: Эта стратегия гарантирует строгую согласованность, которую она достигает, используя «мягкие» блокировки: когда обновляется кэшированная сущность, на нее накладывается мягкая блокировка, которая снимается после коммита транзакции. Все параллельные транзакции, которые пытаются получить доступ к записям в кэше с наложенной мягкой блокировкой, не смогут их прочитать или записать и отправят запрос в БД. Ehcache использует эту стратегию по умолчанию. + +- TRANSACTIONAL: полноценное разделение транзакций. Каждая сессия и каждая транзакция видят объекты, как если бы только они с ним работали последовательно одна транзакция за другой. Плата за это — блокировки и потеря производительности. + +__Представление объектов в кэше__ + +Еще одна важная деталь про кэш второго уровня о которой стоило бы упомянуть — Hibernate не хранит сами объекты Ваших классов. Он хранит информацию в виде массивов строк, чисел и т.д. Что очень разумно, учитывая сколько лишней памяти занимает каждый объект. Идентификатор объекта выступает указателем на эту информацию. Концептуально это нечто вроде Map, в которой id объекта — ключ, а массивы данных — значения полей. Приблизительно это можно представить себе так: + +1 -> { "Pupkin", 1, null , {1,2,5} } + +Помимо вышесказанного, следует помнить — зависимости Вашего класса по умолчанию также не кэшируются. Например, рассмотрим класс SharedDoc, который кэшируется: +```java +@Entity +@Table(name = "shared_doc") +@Cacheable +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +public class SharedDoc{ +private Set users; +} +``` + +В примере выше при выборке сущности SharedDoc из кэша, коллекция users будет доставаться из БД, а не из кэша второго уровня. Если мы хотим также кэшировать и зависимости, то над полями тоже нужно разместить аннотации @Cacheable и @Cache: +```java +@Entity +@Table(name = "shared_doc") +@Cacheable +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +public class SharedDoc{ +@Cacheable +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +private Set users; +} +``` + +Однако, при кэшировании коллекций, содержащих другие сущности, будут закэшированы только их первичные ключи. Если это коллекция базовых типов, то будут храниться сами значения базовых типов. + +__@Cache__ + +Это аннотация Hibernate, настраивающая тонкости кэширования объекта в кэше второго уровня Hibernate. @Cache принимает три параметра: + +- include - имеет по умолчанию значение all и означающий кэширование всего объекта. Второе возможное значение - non-lazy, запрещает кэширование лениво загружаемых объектов. Кэш первого уровня не обращает внимания на эту директиву и всегда кэширует лениво загружаемые объекты. + +- region - позволяет задать имя региона кэша для хранения сущности. Регион можно представить как разные области кэша, имеющие разные настройки на уровне реализации кэша. Например, можно было бы создать в конфигурации ehcache два региона, один с краткосрочным хранением объектов, другой с долгосрочным и отправлять часто изменяющиеся объекты в первый регион, а все остальные - во второй. Ehcache по умолчанию создает регион для каждой сущности с именем класса этой сущности, соответственно в этом регионе хранятся только эти сущности. К примеру, экземпляры Foo хранятся в Ehcache в кэше с именем “com.baeldung.hibernate.cache.model.Foo”. + +- usage - задаёт стратегию одновременного доступа к объектам. + +Кэш второго уровня создается в области фабрики EntityManagerFactory и доступен для использования во всех EntityManager, которые создаются с использованием этой конкретной фабрики. +Это также означает, что после закрытия фабрики весь кэш, связанный с ним, умирает, а менеджер кэша также закрывается. +Кроме того, это также означает, что если у вас есть два экземпляра фабрики, в вашем приложении будет два менеджера кэша, и при доступе к кэшу, хранящемуся в физическом хранилище, вы можете получить непредсказуемые результаты, такие как пропадание кеша. ++ Всякий раз, когда сессия пытается загрузить объект, самое первое место, где он ищет кэшированную копию объекта в кэше первого уровня. ++ Если кэшированная копия объекта присутствует в кэше первого уровня, она возвращается как результат метода загрузки. ++ Если в кэше первого уровня нет кэшированной сущности, то для кэшированной сущности ищется кэш второго уровня. ++ Если кэш второго уровня имеет кэшированный объект, он возвращается как результат метода load(). Но перед возвратом объекта он также сохраняется в кэше первого уровня, так что при следующем вызове метода загрузки объект будет возвращен из самого кэша первого уровня, и больше не потребуется обращаться в кэш второго уровня. ++ Если объект не найден в кэше первого уровня и кэше второго уровня, то выполняется запрос к базе данных, и объект сохраняется на обоих уровнях кэша перед возвратом в качестве ответа метода load(). ++ Кэш второго уровня проверяет себя для измененных объектов. ++ Если какой-либо пользователь или процесс вносят изменения непосредственно в базу данных, то само по себе кэширование второго уровня не может обновляться до тех пор, пока не истечет время «timeToLiveSeconds» для этой области кэша. В этом случае хорошей идеей будет сделать недействительным весь кеш и позволить hibernate снова построить кэш. + +@Cacheable это аннотация JPA и позволяет объекту быть закэшированным. Hibernate поддерживает эту аннотацию в том же ключе. +@Cache это аннотация Hibernate, настраивающая тонкости кэширования объекта в кэше второго уровня Hibernate. Аннотации @Cacheable достаточно, чтобы объект начал кэшироваться с настройками по умолчанию. При этом @Cache использованная без @Cacheable не разрешит кэширование объекта. Для работы с кэшем второго уровня (second level cache) в JPA описан Cache интерфейс, содержащий большое количество методов по управлению кэшем второго уровня (second level cache), если он поддерживается провайдером JPA, конечно. Объект данного интерфейса можно получить с помощью метода getCache у EntityManagerFactory. -__Кеш запросов__ — Кеш запросов похож на кеш второго уровня. Но в отличии от него — ключом к данным кеша выступает не идентификатор объекта, а совокупность параметров запроса. А сами данные — это идентификаторы объектов соответствующих критериям запроса. Таким образом, этот кеш рационально использовать с кешем второго уровня. Он тоже по умолчанию отключен. Для включения нужно добавить следующую строку в конфигурационный файл: ++ Сохранение или обновление элемента: save(), update(), saveOrUpdate() ++ Получение предмета:load(), get(), list(), iterate(), scroll() +Состояние объекта синхронизируется с базой данных при вызове метода flush(). Чтобы избежать этой синхронизации, вы можете удалить объект и все коллекции из кэша первого уровня с помощью evict() метода. Чтобы удалить все элементы из кэша сеанса, используйте метод Session.clear(): +` +ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set +while ( cats.next() ) { + Cat cat = (Cat) cats.get(0); + doSomethingWithACat(cat); + sess.evict(cat); +} +` +Определение того, принадлежит ли элемент кешу сеанса. Сеанс предоставляет contains() метод для определения того, принадлежит ли экземпляр кешу сеанса. + +__Кеш запросов__ — QueryCache, Кеш запросов похож на кеш второго уровня. Но в отличии от него — ключом к данным кеша выступает не идентификатор объекта, а совокупность параметров запроса. А сами данные — это идентификаторы объектов соответствующих критериям запроса. Таким образом, этот кеш рационально использовать с кешем второго уровня. Он тоже по умолчанию отключен. Для включения нужно добавить следующую строку в конфигурационный файл: + +в файле persistence.xml, установив для параметра `` +и определив +`hibernate.cache.region.factory_class` +Кроме того, вам также необходимо активировать кэширование для конкретного запроса, для которого вы хотите кэшировать результаты, вызывая +`setCacheable(true)` + +или через подсказку в запросе setHint("org.hibernate.cacheable", true): +```java +entityManager.createQuery("select f from Foo f") +.setHint("org.hibernate.cacheable", true) +.getResultList(); +``` + +У кэша запросов есть и своя цена — Hibernate будет вынужден отслеживать сущности закешированные с определённым запросом и выкидывать запрос из кэша, если кто-то поменяет значение сущности. То есть для кэша запросов стратегия параллельного доступа всегда read-only. + +## Для чего нужна аннотация @Cacheable? +@Cacheable - аннотация JPA, используется для указания того, должна ли сущность храниться в кэше второго уровня, в случае, если в файле persistence.xml (или в свойстве javax.persistence.sharedCache.mode конфигурационного файла) для элемента shared-cache-mode установлено одно из значений: +- ENABLE_SELECTIVE: только сущности с аннотацией @Cacheable (равносильно значению по умолчанию @Cacheable(value=true)) будут сохраняться в кэше второго уровня. +- DISABLE_SELECTIVE: все сущности будут сохраняться в кэше второго уровня, за исключением сущностей, помеченных аннотацией @Cacheable(value=false)как некэшируемые. +- ALL: сущности всегда кэшируются, даже если они помечены как некэшируемые. +- NONE: ни одна сущность не кэшируется, даже если помечена как кэшируемая. При данной опции имеет смысл вообще отключить кэш второго уровня. +- UNSPECIFIED: применяются значения по умолчанию для кэша второго уровня, определенные Hibernate. Это эквивалентно тому, что вообще не используется shared-cache-mode, так как Hibernate не включает кэш второго уровня, если используется режим UNSPECIFIED. +Аннотация @Cacheable размещается над классом сущности. Её действие распространяется на эту сущность и её наследников, если они не определили другое поведение + +## Hibernate proxy (lazy load). +Hibernate использует прокси объект для поддержки отложенной загрузки. Обычно при загрузке данных из таблицы Hibernate не загружает все отображенные (замаппинные) объекты. Как только вы ссылаетесь на дочерний объект или ищите объект с помощью геттера, если связанная сущность не находиться в кэше сессии, то прокси код перейдет к базе данных для загрузки связанной сущности. Для этого используется javassist, чтобы эффективно и динамически создавать реализации подклассов ваших entity объектов. + ++ LAZY - не грузит связанные сущности, только при обращении ++ EAGER - грузит сразу все связанные сущности + +Например, есть Owner(один) и Book(много). +```java +@Entity +@Table(name = "owners") +public class Owner implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "owner_id", nullable = false, unique = true) + private Long id; + + @Column(name = "owner_name", nullable = false) + private String name; + + @OneToMany(fetch = FetchType.LAZY,mappedBy = "owner") + private Set books= new HashSet<>(0); + + public Worker() { + } +} + +@Entity +@Table(name = "books") +public class Book implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "book_id", unique = true, nullable = false) + private Long id; + + @Column(name = "book_name", nullable = false, unique = true) + private String name; + + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "owner_id") + private Owner owner; + + public Task() { + } +} +``` +Если сохранить, то все будет ок. Но если читать Owner, то будет LazyInitializationException. Потому что fetch = FetchType.LAZY - что хибернейт не будет инициализировать эти поля пока вы к ним не обратитесь. Но т.к. вы обращаетесь к этим полям за пределами транзакционных методов, он не может это сделать и выкидывает ошибку. Чтобы этого избежать надо, что метод, который обращается к этим полям был с аннотацей Transactional. Или добавить Join fetch запрос + +Либо можно немного изменить реализацию OwnerServiceImpl.read(). Сделать такое: +```java +@Override +public Owner read(Long id) { + Owner owner = ownerRepository.findOne(id); + owner.getBooks().iterator(); + return owner; +} +``` +Или Hibernate.initialize(owner.getBooks()); Это костыль, но он заставит хибернейт инициировать коллекцию. ## Стратегии кеширования? Стратегии кеширования определяют поведения кеша в определенных ситуациях. Выделяют четыре группы: @@ -997,17 +1561,160 @@ __Кеш запросов__ — Кеш запросов похож на кеш + Nonstrict-read-write — аналогичен read-write, но изменения объектов могут запаздывать и транзакции могут видеть старые версии объектов. Рекомендуется использовать в случаях, когда одновременное обновление объектов маловероятно и не может привести к проблемам. + Transactional — полноценное разделение транзакций. Каждая сессия и каждая транзакция видят объекты, как если бы только они с ним работали последовательно одна транзакция за другой. Плата за это — блокировки и потеря производительности. -## Что такое Mapped Superclass? -Mapped Superclass это класс от которого наследуются Entity, он может содержать аннотации JPA, однако сам такой класс не является Entity, ему не обязательно выполнять все требования установленные для Entity (например, он может не содержать первичного ключа). Такой класс не может использоваться в операциях EntityManager или Query. Такой класс должен быть отмечен аннотацией MappedSuperclass или соответственно описан в xml файле. - ## Какие три типа стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA? -В JPA описаны три стратегии наследования мапинга (Inheritance Mapping Strategies), то есть как JPA будет работать с классами-наследниками Entity: +Стратегии наследования нужны для того, чтобы дать понять провайдеру(Hibernate) как ему отображать в БД сущности-наследники. Для этого нам нужно декорировать родительский класс аннотацией @Inheritance и указать один из типов отображения: SINGLE_TABLE, TABLE_PER_CLASS, JOINED. -1) __одна таблица на всю иерархию наследования (a single table per class hierarchy)__ — все enity, со всеми наследниками записываются в одну таблицу, для идентификации типа entity определяется специальная колонка “discriminator column”. Например, если есть entity Animals c классами-потомками Cats и Dogs, при такой стратегии все entity записываются в таблицу Animals, но при это имеют дополнительную колонку animalType в которую соответственно пишется значение «cat» или «dog».Минусом является то что в общей таблице, будут созданы все поля уникальные для каждого из классов-потомков, которые будет пусты для всех других классов-потомков. Например, в таблице animals окажется и скорость лазанья по дереву от cats и может ли пес приносить тапки от dogs, которые будут всегда иметь null для dog и cat соответственно. +1. SINGLE_TABLE. Одна таблица на всю иерархию классов. +2. TABLE_PER_CLASS. Таблица для каждого конкретного класса сущностей. +3. JOINED. Стратегия «соединения», при которой поля или свойства, специфичные + для подклассов, отображаются в таблицах этих подклассов, а поля или свойства + родительского класса отображаются в таблице родительского класса. + +1) __одна таблица на всю иерархию наследования (a single table per class hierarchy)__ — Является стратегией по умолчанию и используется, когда аннотация @Inheritance не указана в родительском классе или когда она указана без конкретной стратегии. все enity, со всеми наследниками записываются в одну таблицу, для идентификации типа entity определяется специальная колонка “discriminator column”. Например, если есть entity Animals c классами-потомками Cats и Dogs, при такой стратегии все entity записываются в таблицу Animals, но при это имеют дополнительную колонку animalType в которую соответственно пишется значение «cat» или «dog».Минусом является то что в общей таблице, будут созданы все поля уникальные для каждого из классов-потомков, которые будет пусты для всех других классов-потомков. Например, в таблице animals окажется и скорость лазанья по дереву от cats и может ли пес приносить тапки от dogs, которые будут всегда иметь null для dog и cat соответственно. + +Еще пример, если есть entity Employee c классами-потомками FullTimeEmployee и PartTimeEmployee, то при такой стратегии все FullTimeEmployee и PartTimeEmployee записываются в таблицу Employee, и при этом в таблице появляется дополнительная колонка с именем DTYPE, в которой будут записаны значения, определяющие принадлежность к классу. По умолчанию эти значения формируются из имён классов, в нашем случае - либо «FullTimeEmployee» либо «PartTimeEmployee». Но мы можем их поменять в аннотации у каждого класса-наследника:@DiscriminatorValue("F") + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate4.png) + +Если мы хотим поменять имя колонки, то мы должны указать её новое имя в параметре аннотации у класса-родителя: @DiscriminatorColumn(name=EMP_TYPE). +```java +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@Entity +@DiscriminatorColumn(name = "EMP_TYPE") +public class Employee { + @Id + @GeneratedValue + private long id; + private String name; +} +@Entity +@DiscriminatorValue("F") +public class FullTimeEmployee extends Employee { + private int salary; +} +@Entity +@DiscriminatorValue("P") +public class PartTimeEmployee extends Employee { + private int hourlyRate; +} +``` +Эта стратегия обеспечивает хорошую поддержку полиморфных отношений +между сущностями и запросами, которые охватывают всю иерархию классов +сущностей +```java +-- Persisting entities -- +FullTimeEmployee{id=0, name='Sara', salary=100000} +PartTimeEmployee{id=0, name='Tom', hourlyRate='60'} +-- Native queries -- +'Select * from Employee' +[F, 1, Sara, null, 100000] +[P, 2, Tom, 60, null] +-- Loading entities -- +FullTimeEmployee{id=1, name='Sara', salary=100000} +PartTimeEmployee{id=2, name='Tom', hourlyRate='60'} +``` +Минусом стратегии является невозможность применения ограничения NOT NULL для тех колонок таблицы, которые характерны только для классов-наследников. 2) __объединяющая стратегия (joined subclass strategy)__ — в этой стратегии каждый класс enity сохраняет данные в свою таблицу, но только уникальные колонки (не унаследованные от классов-предков) и первичный ключ, а все унаследованные колонки записываются в таблицы класса-предка, дополнительно устанавливается связь (relationships) между этими таблицами, например в случае классов Animals (см.выше), будут три таблицы animals, cats, dogs, причем в cats будет записана только ключ и скорость лазанья, в dogs — ключ и умеет ли пес приносить палку, а в animals все остальные данные cats и dogs c ссылкой на соответствующие таблицы. Минусом тут являются потери производительности от объединения таблиц (join) для любых операций. -3) __одна таблица для каждого класса (table per concrete class strategy)__ — тут все просто каждый отдельный класс-наследник имеет свою таблицу, т.е. для cats и dogs (см.выше) все данные будут записываться просто в таблицы cats и dogs как если бы они вообще не имели общего суперкласса. Минусом является плохая поддержка полиморфизма (polymorphic relationships) и то что для выборки всех классов иерархии потребуются большое количество отдельных sql запросов или использование UNION запроса. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate5.png) + +Столбец первичного ключа в таблице подкласса служит внешним ключом первичного ключа таблицы суперкласса. Также в таблице родительского класса добавляется столбец DiscriminatorColumn с DiscriminatorValue для определения типа наследника. +```java +@Inheritance(strategy = InheritanceType.JOINED) +@Entity +@DiscriminatorColumn(name = "EMP_TYPE") //определение типа наследника +public class Employee { +@Id +@GeneratedValue +private long id; +private String name; +............. +} +@Entity +@DiscriminatorValue("F") +@Table(name = "FULL_TIME_EMP") +public class FullTimeEmployee extends Employee { +private int salary; +............. +} +@Entity +@DiscriminatorValue("P") +@Table(name = "PART_TIME_EMP") +public class PartTimeEmployee extends Employee { +private int hourlyRate; +............. +} + +-- Persisting entities -- +FullTimeEmployee{id=0, name='Sara', salary=100000} +PartTimeEmployee{id=0, name='Robert', hourlyRate='60'} +-- Native queries -- +'Select * from Employee' +[F, 1, Sara] +[P, 2, Robert] +'Select * from FULL_TIME_EMP' +[100000, 1] +'Select * from PART_TIME_EMP' +[60, 2] +``` + +Эта стратегия обеспечивает хорошую поддержку полиморфных отношений, но требует выполнения одной или нескольких операций соединения таблиц при создании экземпляров подклассов сущностей. В глубоких иерархиях классов это может привести к недопустимому снижению производительности. Точно так же запросы, которые покрывают всю иерархию классов, требуют операций соединения между таблицами подклассов, что приводит к снижению производительности: +```java +-- Loading entities -- +List entityAList = em.createQuery("Select t from Employee t") +.getResultList(); // Hibernate makes joins to assemble entities +FullTimeEmployee{id=1, name='Sara', salary=100000} +PartTimeEmployee{id=2, name='Robert', hourlyRate='60'} +``` + +3) __одна таблица для каждого класса (table per concrete class strategy)__ — каждый отдельный класс-наследник имеет свою таблицу, т.е. для cats и dogs (см.выше) все данные будут записываться просто в таблицы cats и dogs как если бы они вообще не имели общего суперкласса. Минусом является плохая поддержка полиморфизма (polymorphic relationships) и то что для выборки всех классов иерархии потребуются большое количество отдельных sql запросов или использование UNION запроса. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate6.png) + +```java +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@Entity +public class Employee { +@Id +@GeneratedValue +private long id; +private String name; +............. +} +@Entity +@Table(name = "FULL_TIME_EMP") +public class FullTimeEmployee extends Employee { +private int salary; +............. +} +@Entity +@Table(name = "PART_TIME_EMP") +public class PartTimeEmployee extends Employee { +private int hourlyRate; +............. +} +-- Persisting entities -- +FullTimeEmployee{id=0, name='Sara', salary=100000} +PartTimeEmployee{id=0, name='Robert', hourlyRate='60'} +-- Native queries -- +'Select * from Employee' +// no data +'Select * from FULL_TIME_EMP' +[1, Sara, 100000] +'Select * from PART_TIME_EMP' +[2, Robert, 60] +-- Loading entities -- +List entityAList = em.createQuery("Select t from Employee t") +.getResultList(); // Hibernate makes additional sql- or union-queries to get +entities +PartTimeEmployee{id=2, name='Robert', hourlyRate='60'} +FullTimeEmployee{id=1, name='Sara', salary=100000} +``` + +Минусом является плохая поддержка полиморфизма (polymorphic relationships) и то, что для выборки всех классов иерархии потребуется большое количество отдельных sql-запросов для каждой таблицы-наследника или использование UNIONзапроса для соединения таблиц всех наследников в одну таблицу. Также недостатком этой стратегии является повторение одних и тех же атрибутов в таблицах. + +При TABLE PER CLASS не работает стратегия генератора первичных ключей IDENTITY, поскольку может быть несколько объектов подкласса, имеющих один и тот же идентификатор, и запрос базового класса приведет к получению объектов с одним и тем же идентификатором (даже если они принадлежат разным типам). ## Стратегии загрузки объектов в Hibernate? __Join fetching:__ hibernate получает ассоциированные объекты и коллекции одним SELECT используя OUTER JOIN @@ -1021,9 +1728,249 @@ __Batch fetching:__ оптимизированная стратегия вида ## Для чего нужна аннотация Basic? Basic — указывает на простейший тип маппинга данных на колонку таблицы базы данных. Также в параметрах аннотации можно указать fetch стратегию доступа к полю и является ли это поле обязательным или нет. +В широком смысле Hibernate разделяет типы на две группы: +1. Типы значений (Value types). +2. Типы сущностей (Entity types). + +__Типы сущностей__ +Сущности из-за своего уникального идентификатора существуют независимо от других объектов, тогда как типы значений нет. Экземпляры сущностей соответствуют строкам в таблице базы данных и различаются между собой благодаря уникальным идентификаторам. Например, две сущности могут иметь абсолютно одинаковые значения полей, но имея разные идентификаторы (первичные ключи) они будут считаться разными, в отличие от POJO, которые при наличии абсолютно одинаковых значений полей будут считаться равными (equals вернет true). Из-за требования к наличию уникального идентификатора, сущности существуют независимо и определяют свой собственный жизненный цикл. + +__Типы значений__ +Это данные, которые не определяют свой собственный жизненный цикл. По сути, они принадлежат сущности (entity), которая определяет их жизненный цикл. С другой стороны, всё состояние объекта полностью состоит из типов значений. В свою очередь, типы значений подразделяются на три подкатегории: + +1. Базовые типы (Basic types). +2. Встраиваемые типы (Embeddable types). +3. Типы коллекций (Collection types) + + __Базовый тип значений__ +Соответствует одному столбцу в БД. Hibernate предоставляет ряд встроенных базовых типов, которые соответствуют естественным отображениям, рекомендованным спецификациями JDBC. + + Аннотация @Basic может быть применена к полю любого из следующих типов: +- Примитивы и их обертки. +- java.lang.String +- java.math.BigInteger +- java.math.BigDecimal +- java.util.Date +- java.util.Calendar +- java.sql.Date +- java.sql.Time +- java.sql.Timestamp +- byte[] or Byte[] +- char[] or Character[] +- enums +- любые другие типы, которые реализуют Serializable. + +Строго говоря, базовый тип в Hibernate обозначается аннотацией javax.persistence.Basic. Вообще, аннотацию @Basic можно не ставить, как это и происходит по умолчанию. + +Аннотация @Basic определяет 2 атрибута: +1. optional - boolean (по умолчанию true) - определяет, может ли значение поля или свойства быть null. Игнорируется для примитивных типов. Но если тип поля не примитивного типа, то при попытке сохранения сущности будет выброшено исключение. + +2. fetch - FetchType (по умолчанию EAGER) - определяет, должен ли этот атрибут извлекаться незамедлительно (EAGER) или лениво (LAZY). Однако, это необязательное требование JPA, и провайдерам разрешено незамедлительно загружать данные, даже для которых установлена ленивая загрузка. + +Без аннотации @Basic при получении сущности из БД по умолчанию её поля базового типа загружаются принудительно (EAGER) и значения этих полей могут быть null + +## Для чего нужна аннотация Column? +Аннотация @Column сопоставляет поле класса столбцу таблицы, а её атрибуты определяют поведение в этом столбце, используется для генерации схемы базы данных + +@Basic vs @Column: +1. Атрибуты @Basic применяются к сущностям JPA, тогда как атрибуты @Column применяются к столбцам базы данных. +2. @Basic имеет атрибут optional, который говорит о том, может ли поле объекта быть null или нет; с другой стороны атрибут nullable аннотации @Column указывает, может ли соответствующий столбец в таблице быть null. +3. Мы можем использовать @Basic, чтобы указать, что поле должно быть загружено лениво. +4. Аннотация @Column позволяет нам указать имя столбца в таблице и ряд других свойств: + a. insertable/updatable - можно ли добавлять/изменять данные в колонке, по умолчанию true; + b. length - длина, для строковых типов данных, по умолчанию 255 + ## Для чего нужна аннотация Access? Она определяет тип доступа (access type) для класса entity, суперкласса, embeddable или отдельных атрибутов, то есть как JPA будет обращаться к атрибутам entity, как к полям класса (FIELD) или как к свойствам класса (PROPERTY), имеющие гетеры (getter) и сетеры (setter). +Hibernate или другой провайдер должен каким-то образом получать доступ к полям сущности. Например, при сохранении сущности в базу данных Hibernate должен получить доступ к состоянию сущности, то есть прочитать значения полей сущности, чтобы записать их в соответствующие ячейки таблицы. Аналогично при получении данных из БД и формировании из них объекта сущности, Hibernate должен создать этот самый объект сущности (для этого ему и нужен public или protected конструктор без параметров), а затем записать в поля этого объекта значения, полученные из ячеек БД, тем самым сформировав состояние сущности. + +Для чтения и записи этих полей Hibernate использует два подхода: +1. Field access (доступ по полям). При таком способе аннотации маппинга (Id, Column, OneToMany, … ) размещаются над полями, и Hibernate напрямую работает с полями сущности, читая и записывая их. +2. Property access (доступ по свойствам). При таком способе аннотации размещаются над методами-геттерами, но никак не над сеттерами. Hibernate использует их и сеттеры для чтения и записи полей сущности. Но есть требование - у сущности с property access названия методов должны соответствовать требованиям JavaBeans. Например, если у сущности Customer есть поле с именем firstName, то у этой сущности должны быть определены методы getFirstName и setFirstName для чтения и записи поля firstName. + +Совокупность полей и методов (свойств) сущности называется атрибутами. + +Эти два подхода неявно определяют тип доступа к состоянию конкретной сущности - либо доступ по полям либо доступ по свойствам. Но при неявном определении типа доступа JPA требует, чтобы у всех сущностей в иерархии был единый тип доступа. + +По умолчанию тип доступа определяется местом, в котором находится аннотация @Id. Если она будет над полем - это будет AccessType.FIELD, если над геттером - это AccessType.PROPERTY. + +Чтобы явно определить тип доступа у сущности, нужно использовать аннотацию @Access, которая может быть указана у сущности, Mapped Superclass и Embeddable class, а также над полями или методами. + +Аннотация @Access позволяет в иерархии сущностей с одним единым типом доступа безболезненно определить для одной или нескольких сущностей другой тип доступа. То есть в иерархии, где у всех тип доступа, например, field access, можно у какой-нибудь сущности указать тип доступа property access и это не нарушит работу Hibernate. + +Если у сущности объявлена аннотация @Access(AccessType.FIELD): +- значит аннотации маппинга нужно размещать над полями; +- есть возможность у любых атрибутов сущности поменять тип доступа на property access, разместив аннотацию @Access(AccessType.PROPERTY) над соответствующими геттерами; +- разместив аннотации маппинга над методами, не имеющими @Access(AccessType.PROPERTY), получим неопределенное поведение. + +Если у сущности объявлена аннотация @Access(AccessType.PROPERTY): +- значит аннотации маппинга нужно размещать над геттерами; +- есть возможность у любых атрибутов сущности поменять тип доступа на field access, разместив аннотацию @Access(AccessType.FIELD) над соответствующими полями; +- разместив аннотации маппинга над полями, не имеющими @Access(AccessType.FIELD), получим неопределенное поведение. + +Поля, унаследованные от суперкласса, имеют тип доступа этого суперкласса, а не дочерней сущности, даже если они не совпадают. + +Когда у одной сущности определены разные типы доступа, то нужно использовать аннотацию @Transient для избежания дублирования маппинга. + +## Для чего нужны аннотации @Embedded и @Embeddable? +__@Embeddable__ +Аннотация JPA, размещается над классом для указания того, что класс является встраиваемым в другие классы и будет внедрен другими сущностями, то есть поля этого встраиваемого класса будут добавляться к полям других сущностей и будут представлять столбцы в таблице этой сущности. Так, во встраиваемый класс мы можем выделить общие поля для разных сущностей не создавая для него таблицу. Встраиваемый класс сам не является сущностью. +```java +@Embeddable +public class ContactPerson { +private String firstName; +private String lastName; +private String phone; +// standard getters, setters +} +``` + +__@Embedded__ +Аннотация JPA, используется для размещения над полем в классе-сущности для указания того, что мы внедряем встраиваемый класс. +```java +@Entity +public class Company { +@Id +@GeneratedValue +private Integer id; +private String name; +private String address; +private String phone; +@Embedded +private ContactPerson contactPerson; +// standard getters, setters +} +``` + +##Для чего нужны аннотации @JoinColumn, @JoinColumns и @JoinTable? +__@JoinColumn__ + +Аннотация @JoinColumn используется для указания столбца FOREIGN KEY, используемого при установлении связей между сущностями или коллекциями. Мы помним, что только сущность-владелец связи может иметь внешние ключи от другой сущности (владеемой). Однако, мы можем указать аннотацию @JoinColumn как во владеющей таблице, так и во владеемой, но столбец с внешними ключами всё равно появится во владеющей таблице. Особенности использования: + +- @OneToOne: означает, что появится столбец addressId в таблице сущностивладельца связи Office, который будет содержать внешний ключ, ссылающийся на первичный ключ владеемой сущности Address. +```java +@Entity +public class Office { +@OneToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "addressId") +private Address address; +} +``` + +- @OneToMany/@ManyToOne: в данном случае мы можем использовать параметр mappedBy для того, чтобы столбец с внешними ключами находился на владеющей стороне ManyToOne - то есть в таблице Email: +```java +@Entity +public class Employee { + +@Id +private Long id; + +@OneToMany(fetch = FetchType.LAZY, mappedBy = "employee") +private List emails; +} +@Entity +public class Email { + +@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "employee_id") +private Employee employee; +} +``` +В приведенном выше примере таблица Email (владелец связи) имеет столбец employee_id, в котором хранится значение идентификатора и внешний ключ к таблице Employee. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate8.png) +- Если бы мы не указали mappedBy, то была бы создана сводная (третья)таблица с первичными ключами из двух основных таблиц. + +__@JoinColumns__ + +Аннотация @JoinColumns используется для группировки нескольких аннотаций @JoinColumn, которые используются при установлении связей между сущностями или коллекциями, у которых составной первичный ключ и требуется несколько колонок для указания внешнего ключа. + +В каждой аннотации @JoinColumn должны быть указаны элементы name и referencedColumnName: +```java +@ManyToOne +@JoinColumns({ +@JoinColumn(name="ADDR_ID", referencedColumnName="ID"), +@JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP") +}) +public Address getAddress() { return address; } +``` + +__@JoinTable__ + +Аннотация @JoinTable используется для указания связывающей (сводной, третьей) таблицы между двумя другими таблицами. + +## Для чего нужны аннотации @OrderBy и @OrderColumn + +__@OrderBy__ + +Аннотация @OrderBy указывает порядок, в соответствии с которым должны располагаться элементы коллекций сущностей, базовых или встраиваемых типов при их извлечении из БД. Эта аннотация может использоваться с аннотациями @ElementCollection, @OneToMany, @ManyToMany. + +При использовании с коллекциями базовых типов, которые имеют аннотацию @ElementCollection, элементы этой коллекции будут отсортированы в натуральном порядке, по значению базовых типов: +```java +@ElementCollection +@OrderBy +private List phoneNumbers; +``` + +В данном примере коллекция строк phoneNumbers, будет отсортирована в натуральном порядке, по значениям базового типа String. + +Если это коллекция встраиваемых типов (@Embeddable), то используя точку(".") мы можем сослаться на атрибут внутри встроенного атрибута. Например, следующий код отсортируют адреса по названиям стран в обратном порядке: +```java +@Embeddable +public class Address { +... +@Embedded +private City city +.... +} +@ElementCollection +@OrderBy("city.country DESC") +private List
addresses; +``` + +Если это коллекция сущностей, то у аннотации @OrderBy можно указать имя поля сущности, по которому сортировать эти самые сущности: +```java +@Entity +public class Task { +.... +@OneToOne +private Employee supervisor; +... +} +@ManyToMany +@OrderBy("supervisor") +private List tasks +``` + +Если мы не укажем у @OrderBy параметр, то сущности будут упорядочены по первичному ключу. + +В случае с сущностями доступ к полю по точке (".") не работает. Попытка использовать вложенное свойство, например @OrderBy ("supervisor.name") повлечет Runtime Exceprtion. + +__@OrderColumn__ + +Аннотация @OrderColumn создает столбец в таблице, который используется для поддержания постоянного порядка в списке, но этот столбец не считается частью состояния сущности или встраиваемого класса. + +Hibernate отвечает за поддержание порядка как в базе данных при помощи столбца, так и при получении сущностей и элементов из БД. Hibernate отвечает за обновление порядка при записи в базу данных, чтобы отразить любое добавление, удаление или иное изменение порядка, влияющее на список в таблице. + +Аннотация @OrderColumn может использоваться с аннотациями @ElementCollection, @OneToMany, @ManyToMany - указывается на стороне отношения, ссылающегося на коллекцию, которая должна быть упорядочена. В примере ниже Hibernate добавил в таблицу Employee_PhoneNumbers третий столбец PHONENUMBERS_ORDER, который и является результатом работы @OrderColumn: +```java +'Show Columns from Employee_PhoneNumbers' +[EMPLOYEE_ID, BIGINT(19), NO, PRI, NULL] +[PHONENUMBERS, VARCHAR(255), YES, , NULL] +[PHONENUMBERS_ORDER, INTEGER(10), NO, PRI, NULL] +'Select * FROM Employee_PhoneNumbers' +[1, 111-111,111, 0] +[1, 222-222,222, 1] +[2, 333-333-333, 0] +[2, 444-444-444, 1] +[2, 666-666-666, 2] +[3, 555-555-555, 0] +``` + +__@OrderBy vs @OrderColumn__ +Порядок, указанный в @OrderBy, применяется только в рантайме при выполнении запроса к БД, То есть в контексте персистентности, в то время как при использовании @OrderColumn, порядок сохраняется в отдельном столбце таблицы и поддерживается при каждой вставке/обновлении/удалении элементов. + ## Для чего нужны callback методы в JPA? К каким сущностям применяются аннотации callback методов? Перечислите семь callback методов (или, что тоже самое, аннотаций callback методов) Callback методы служат для вызова при определенных событиях Entity (то есть добавить обработку например удаления Entity методами JPA), могут быть добавлены к entity классу, к mapped superclass, или к callback listener классу, заданному аннотацией EntityListeners (см предыдущий вопрос). Существует семь callback методов (и аннотаций с теми же именами): 1) PrePersist @@ -1035,11 +1982,6 @@ Callback методы служат для вызова при определен 7) PostLoad ## Какие видов блокировок (lock) описаны в спецификации JPA? -В оптимистичных блокировках при коммите в базу данных производится сравнивание значения поля, помеченного как version, на момент получения данных и на данный момент. Если оно изменилось, то есть какая-то другая транзакция опередила нашу и успела изменить данные, то в таком случае наша транзакция выбрасывает ошибку, и необходимо заново запускать ее. - -В пессимистичных же блокировка накладывается сразу же перед предполагаемой модификацией данных на все строки, которые такая модификация предположительно затрагивает. - -LockModeType задает стратегию блокирования. У JPA есть шесть видов блокировок, перечислим их в порядке увеличения надежности (от самого ненадежного и быстрого, до самого надежного и медленного): 1) NONE — без блокировки @@ -1049,18 +1991,108 @@ LockModeType задает стратегию блокирования. 5) PESSIMISTIC_WRITE — пессимистичная блокировка на запись (и чтение), 6) PESSIMISTIC_FORCE_INCREMENT — пессимистичная блокировка на запись (и чтение) с принудительным увеличением поля версионности. +__Оптимистичное блокирование__ предполагает, что параллельно выполняющиеся транзакции редко обращаются к одним и тем же данным и позволяет им спокойно и свободно выполнять любые чтения и обновления данных. Но при окончании транзакции производится проверка, изменились ли данные в ходе выполнения данной транзакции и, если да, транзакция обрывается и выбрасывается исключение. Оптимистичное блокирование в JPA реализовано путём внедрения в сущность специального поля версии: +```java +@Entity +public class Company extends AbstractIdentifiableObject { +@Version +private long version; +@Getter +@Setter +private String name; +@Getter +@Setter +@ManyToMany(mappedBy = "workingPlaces") +private Collection workers; +} +``` + +Поле, аннотирование @Version, может быть целочисленным или временнЫм. При завершении транзакции, если сущность была оптимистично заблокирована, будет проверено, не изменилось ли значение @Version кем-либо ещё, после того как данные были прочитаны, и, если изменилось, будет выкинуто OptimisticLockException. Использование этого поля позволяет отказаться от блокировок на уровне базы данных и сделать всё на уровне JPA, улучшая уровень конкурентности. + +JPA поддерживает два типа оптимистичной блокировки: + +- LockModeType.OPTIMISTIC — блокировка на чтение, которая работает, как описано выше: если при завершении транзакции кто-то извне изменит поле @Version, то транзакция автоматически будет откачена и будет выброшено OptimisticLockException. +- LockModeType.OPTIMISTIC_FORCE_INCREMENT — блокировка на запись. Ведёт себя как и блокировка на чтение, но при этом увеличивает значение поля @Version. + +Обе блокировки ставятся путём вызова метода lock() у EntityManager, в который передаётся сущность, требующая блокировки и уровень блокировки: +```java +EntityManager em = entityManagerFactory.createEntityManager(); +em.lock(company1, LockModeType.OPTIMISTIC); +em.lock(company2, LockModeType.OPTIMISTIC_FORCE_INCREMENT); +``` +Блокировка будет автоматически снята при завершении транзакции, снять её до этого вручную невозможно. + +__Пессимистичное блокирование__ напротив, ориентирован на транзакции, которые постоянно или достаточно часто конкурируют за одни и те же данные и поэтому блокирует доступ к данным превентивно, в тот момент когда читает их. Другие транзакции останавливаются, когда пытаются обратиться к заблокированным данным и ждут снятия блокировки (или кидают исключение). Пессимистичное блокирование выполняется на уровне базы и поэтому не требует вмешательств в код сущности. Так же, как и в случае с оптимистичным блокированием, поддерживаются блокировки чтения и записи: + +- LockModeType.PESSIMISTIC_READ — данные блокируются в момент чтения и это гарантирует, что никто в ходе выполнения транзакции не сможет их изменить. Остальные транзакции, тем не менее, смогут параллельно читать эти данные. Использование этой блокировки может вызывать долгое ожидание блокировки или даже выкидывание PessimisticLockException. +- LockModeType.PESSIMISTIC_WRITE — данные блокируются в момент записи и никто с момента захвата блокировки не может в них писать и не может их читать до окончания транзакции, владеющей блокировкой. Использование этой блокировки может вызывать долгое ожидание блокировки. + +- Кроме того, для сущностей с полем, аннотированным @Version, существует +третий вариант пессимистичной блокировки: __LockModeType.PESSIMISTIC_FORCE_INCREMENT__ — ведёт себя как LockModeType.PESSIMISTIC_WRITE, но в конце транзакции увеличивает значение поля @Version, даже если фактически сущность не изменилась. Накладываются пессимистичные блокировки так же как и оптимистичные, вызовом метода lock(): +```java +em.lock(company1, LockModeType.PESSIMISTIC_READ); +em.lock(company2, LockModeType.PESSIMISTIC_WRITE); +em.lock(company3, LockModeType.PESSIMISTIC_FORCE_INCREMENT); +``` + +Снимаются они тоже автоматически, по завершению транзакции. + +Следующие публичные методы EntityManager-а могут использоваться для +наложения блокировок: + +```java +void lock(Object entity, LockModeType lockMode) +void lock(Object entity, LockModeType lockMode, Map +properties) + T find(Class entityClass, Object primaryKey, LockModeType +lockMode) + T find(Class entityClass, Object primaryKey, LockModeType +lockMode, Map properties) +void refresh(Object entity, LockModeType lockMode) +void refresh(Object entity, LockModeType lockMode, Map +properties) +``` + +Помимо вышеуказанных методов, в Query API также есть методы для определения блокировок + ## Как можно изменить настройки fetch стратегии любых атрибутов Entity для отдельных запросов (query) или методов поиска (find), то если у Enity есть атрибут с fetchType = LAZY, но для конкретного запроса его требуется сделать EAGER или наоборот? Для этого существует EntityGraph API, используется он так: с помощью аннотации NamedEntityGraph для Entity, создаются именованные EntityGraph объекты, которые содержат список атрибутов у которых нужно поменять fetchType на EAGER, а потом данное имя указывается в hits запросов или метода find. В результате fetchType атрибутов Entity меняется, но только для этого запроса. Существует две стандартных property для указания EntityGraph в hit: +```java +@NamedEntityGraphs({ + @NamedEntityGraph( + name = 'customer.products', + attributeNodes = { + @NamedAttributeNode("products") + } + ) +}) +@Entity +@Table(name = "customer") +``` + 1) javax.persistence.fetchgraph — все атрибуты перечисленные в EntityGraph меняют fetchType на EAGER, все остальные на LAZY 2) javax.persistence.loadgraph — все атрибуты перечисленные в EntityGraph меняют fetchType на EAGER, все остальные сохраняют свой fetchType (то есть если у атрибута, не указанного в EntityGraph, fetchType был EAGER, то он и останется EAGER)С помощью NamedSubgraph можно также изменить fetchType вложенных объектов Entity. ```java -EntityGraph fetchAuthors = em.createEntityGraph(Book.class); +final EntityGraph entityGraph = session //чтобы использовать EntityGraph, нужно достать его из EntityManager + .getEntityManagerFactory() + .createEntityManager() + .getEntityGraph('customer.products'); +final Customer customer = session + .createQuery(HQL, Customer.class) + .setParameter("id", customerId) + .setHint(javax.persistence.fetchgraph, entityGraph) + .getSingleResult; +//или +EntityGraph fetchAuthors = em.createEntityGraph(Book.class); //чтобы использовать EntityGraph, нужно достать его из EntityManager fetchAuthors.addSubgraph(Book_.authors); List books = em.createQuery("select b from Book b order by b.publicationDate") .setHint("javax.persistence.fetchgraph", fetchAuthors) .getResultList(); assertEquals(3, books.size()); ``` +В итоге в запрос добавится left outer join для получения связанной сущности + +[к оглавлению](#jdbc) ## Что означает полиморфизм (polymorphism) в запросах JPQL (Java Persistence query language) и как его «выключить»? В отличии от SQL в запросах JPQL есть автоматический полиморфизм, то есть каждый запрос к Entity возвращает не только объекты этого Entity, но так же объекты всех его классов-потомков, независимо от стратегии наследования (например, запрос select * from Animal, вернет не только объекты Animal, но и объекты классов Cat и Dog, которые унаследованы от Animal). Чтобы исключить такое поведение используется функция TYPE в where условии (например select * from Animal a where TYPE(a) IN (Animal, Cat) уже не вернет объекты класса Dog). @@ -1070,6 +2102,8 @@ assertEquals(3, books.size()); 2) JPA категорически требует не использовать final классы, Hibernate лишь рекомендует не использовать такие классы чтобы он мог создавать прокси для ленивой загрузки, однако позволяет либо выключить прокси Proxy(lazy=false), либо использовать в качестве прокси интерфейс, содержащий все методы маппинга для данного класса (аннотацией Proxy(proxyClass=интерфейс.class) ) +[к оглавлению](#jdbc) + ## Каскадирование При наличии зависимостей (связей) между сущностями необходимо определить влияние различных операций одной сущности на связанные. Это можно реализовать с помощью аннотации каскадных связей @Cascade. @@ -1082,8 +2116,80 @@ assertEquals(3, books.size()); + LOCK : передает в Hibernate native LOCK действие; + REPLICATE : передает в Hibernate native REPLICATE действие. +__Удаление сирот в отношениях (Orphan Removal)__ + +Представим, что у нас есть класс Customer, у которого есть коллекция Order: +```java +@Entity +public class Customer { + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + private List orders = new ArrayList<>(); + // other mappings, getters and setters +} +``` +Пусть у нас есть один объект Customer - родитель, в коллекции которого есть 4 объекта Order - дети, и мы установили атрибут orphanRemoval = true над этой коллекцией. В нашей базе данных в таблице Customer будет одна строка, а в таблице Order будет четыре строки. Также в таблице Order будет колонка с внешними ключами на таблицу Customer. В каждой из четырех ячеек этой колонки будут ссылки на один и тот же первичный ключ объекта Customer. + +Например, мы удалим из коллекции orders один объект Order - любой из четырех, в результате чего у объекта Customer останется три объекта Order: +```java +Customer customer = entityManager.find(Customer.class, 1L); +Order order = customer.getOrders().get(0); +customer.getOrders().remove(order); +flushAndClear(); +``` + +После запуска метода flushAndClear() - обновления объекта Customer отправятся в базу данных, и произойдет следующее: + +1. Hibernate заметит, что у объекта Customer уже не 4, а 3 связанных дочерних объекта Order; +2. в связи с этим Hibernate найдёт в таблице Order строку с удаленным объектом из коллекции Order; +3. очистит в этой строке ячейку с внешним ключом на Customer; +4. после чего удалит саму эту строку, как осиротевшую (более не ссылающуюся на родителя). + +Если не будет атрибута orphanRemoval = true, то пункт 4 не выполнится, и в таблице Order останется сущность Order, не связанная ни с одной сущностью Customer, то есть её ячейка с внешним ключом будет пустой. Такая сущность будет считаться осиротевшей. + ## Как определить владельца связи? -Для того, чтобы объявить сторону, которая не несет ответственности за отношения, используется атрибут mappedBy. Он ссылается на имя свойства связи на стороне владельца. +Существуют следующие четыре типа связей между сущностями: +1. OneToOne - когда один экземпляр Entity может быть связан не больше чем с одним экземпляром другого Entity. +2. OneToMany - когда один экземпляр Entity может быть связан с несколькими экземплярами других Entity. +3. ManyToOne - обратная связь для OneToMany. Несколько экземпляров Entity могут быть связаны с одним экземпляром другого Entity. +4. ManyToMany - экземпляры Entity могут быть связаны с несколькими экземплярами друг друга. + +__mappedBy__ + Владеемая сторона в двунаправленных отношениях должна ссылаться на владеющую сторону используя элемент mappedBy аннотаций @OneToOne, @OneToMany, или @ManyToMany. Элемент mappedBy определяет поле в объекте, который является владельцем отношения. Если применить атрибут mappedBy на одной стороне связи, то Hibernate не станет создавать сводную таблицу: +```java +@Entity +@Table(name="CART") +public class Cart { + //... + @OneToMany(mappedBy="cart") + private Set items; + // getters and setters +} +@Entity +@Table(name="ITEMS") +public class Items { + //... + @ManyToOne + @JoinColumn(name="cart_id", nullable=false) + private Cart cart; + public Items() {} + // getters and setters +} +``` +В данном примере таблица класса Items является владеющей стороной и будет иметь колонку с внешними ключами на таблицу Cart. Таблица класса Cart будет владеемой. + +__@ManyToOne__ +Сторона many в отношениях many-to-one всегда является владельцем отношений и не может определять элемент mappedBy (такого параметра у аннотации @ManyToOne просто нет). + +__@OneToOne__ +Для двунаправленных отношений one-to-one, сторона-владелец это та сторона, чья таблица имеет столбец с внешним ключом на другую таблицу. Если не указан параметр mappedBy, то колонки с айдишниками появляются у каждой таблицы. + +__@ManyToMany__ +Для двунаправленных отношений many-to-many, любая сторона может быть стороной-владельцем. + +__Запросы и направление отношений__ +Язык запросов Java Persistence и запросы API Criteria часто перемещаются между отношениями. Направление отношений определяет, может ли запрос перемещаться от одной сущности к другой. Например в двунаправленных отношениях запрос может перемещаться как от первой сущности ко второй, так и обратно. В однонаправленных отношениях запрос может перемещаться только в одну сторону - от владеющей сущности к владеемой + +[к оглавлению](#jdbc) ## Что происходит с таблицами при изпользовании mappedBy? В OneToOne без mappedBy хибер явно указывает двустороннюю связь. У Юзера есть колонка с Машинами, у каждой машины есть колонка с Юзером. В итоге если у одного юзера 3 машины, но будут сохраненны 3 строчки разных машин у которых будет одинаковый Юзер. @@ -1094,69 +2200,760 @@ assertEquals(3, books.size()); При использовании mappedBy смежных таблиц нет, т.к. хибер уже точно значет кто на что ссылается. +[к оглавлению](#jdbc) + ## FetchType стратегии по-умолчанию? -Исторически Hibernate по умолчанию использует режим EAGER загрузки в отношении ManyToOne и OneToOne, а во всех остальных случаях — LAZY. Рекомендуется использовать LAZY во всех случаях. Указать в запросе делать join вместо нескольких select-ов всегда возможно, а обратно — отключить EAGER для определённых случаев — нет. +В JPA описаны два типа fetch-стратегии: +1. LAZY — данные поля сущности будут загружены только во время первого обращения к этому полю. +2. EAGER — данные поля будут загружены немедленно вместе с сущностью. + +FetchType.EAGER: Hibernate должен сразу загрузить соответствующее аннотированное поле или свойство. Это поведение по умолчанию для полей, аннотированных @Basic, @ManyToOne и @OneToOne (все что быстро). + +FetchType.LAZY: Hibernate может загружать данные не сразу, а при первом обращении к ним, но так как это необязательное требование, то Hibernate имеет право изменить это поведение и загружать их сразу. Это поведение по умолчанию для полей, аннотированных @OneToMany, @ManyToMany и @ElementCollection (все что медленно) . + +Раньше у Hibernate все поля были LAZY, но в последних версиях - всё как в JPA +[к оглавлению](#jdbc) + +## Enum значения в БД? +__По порядковым номерам EnumType.ORDINAL__ +Если мы сохраняем в БД сущность, у которой есть поле-перечисление (Enum), то в таблице этой сущности создаётся колонка для значений этого перечисления и по умолчанию в ячейки сохраняется порядковый номер этого перечисления (ordinal). +```java +public enum MyEnum { +ConstA, ConstB, ConstC +} + +@Entity +public class MyEntity { +@Id +private long myId; +private MyEnum myEnum; +public MyEntity() { +} +public MyEntity(long myId, MyEnum myEnum) { +this.myId = myId; +this.myEnum = myEnum; +} +............. +} +``` + +В JPA типы Enum могут быть помечены аннотацией @Enumerated, которая может принимать в качестве атрибута EnumType.ORDINAL или EnumType.STRING, определяющий, отображается ли перечисление (enum) на столбец с типом Integer или String соответственно. + +__@Enumerated(EnumType.ORDINAL)__ - значение по умолчанию, говорит о том, что +в базе будут храниться порядковые номера Enum (0, 1, 2…). Проблема с этим типом +отображения возникает, когда нам нужно изменить наш Enum. Если мы добавим новое +значение в середину или просто изменим порядок перечисления, мы сломаем +существующую модель данных. Такие проблемы могут быть трудно уловимыми, и нам +придется обновлять все записи базы данных. + +__По именам EnumType.STRING__ +@Enumerated(EnumType.STRING) - означает, что в базе будут храниться имена Enum. С @Enumerated(EnumType.STRING) мы можем безопасно добавлять новые значения перечисления или изменять порядок перечисления. Однако переименование значения enum все равно нарушит работу базы данных. Кроме того, даже несмотря на то, что это представление данных гораздо более читаемо по сравнению с параметром @Enumerated(EnumType.ORDINAL), оно потребляет намного больше места, чем необходимо. Это может оказаться серьезной проблемой, когда нам нужно иметь дело с большим объемом данных. + +__@PostLoad и @PrePersist__ +Другой вариант - использование стандартных методов обратного вызова из JPA. Мы можем смапить наши перечисления в БД и обратно в методах с аннотациями @PostLoad и @PrePersist. + +Идея состоит в том, чтобы в сущности иметь не только поле с Enum, но и вспомогательное поле. Поле с Enum аннотируем @Transient, а в БД будет храниться значение из вспомогательного поля. Создадим Enum с полем priority. содержащем числовое значение приоритета: +```java +public enum Priority { +LOW(100), MEDIUM(200), HIGH(300); +private int priority; + +private Priority(int priority) { + this.priority = priority; +} + +public int getPriority() { + return priority; +} + +public static Priority of(int priority) { + return Stream.of(Priority.values()) + .filter(p -> p.getPriority() == priority) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } +} +``` + +Мы добавили метод Priority.of(), чтобы упростить получение экземпляра Priority на основе его значения int. Теперь, чтобы использовать его в нашем классе Article, нам нужно добавить два атрибута и реализовать методы обратного вызова: +```java +@Entity +public class Article { +@Id +private int id; +private String title; +@Enumerated(EnumType.ORDINAL) +private Status status; +@Enumerated(EnumType.STRING) +private Type type; +@Basic +private int priorityValue; +@Transient +private Priority priority; + +@PostLoad +void fillTransient() { +if (priorityValue > 0) { +this.priority = Priority.of(priorityValue); +} +} + +@PrePersist +void fillPersistent() { +if (priority != null) { +this.priorityValue = priority.getPriority(); +} +} +} +``` + +Несмотря на то, что этот вариант дает нам бОльшую гибкость по сравнению с ранее описанными решениями, он не идеален. Просто кажется неправильным иметь в сущности целых два атрибута, представляющих одно перечисление. Кроме того, если мы используем этот вариант, мы не сможем использовать значение Enum в запросах JPQL. + +__Converter__ +В JPA с версии 2.1 можно использовать Converter для конвертации Enum’а в некое его значение для сохранения в БД и получения из БД. Все, что нам нужно сделать, это создать новый класс, который реализует javax.persistence.AttributeConverter и аннотировать его с помощью @Converter. +```java +public enum Category { + SPORT("S"), MUSIC("M"), TECHNOLOGY("T"); + private String code; + + private Category(String code) { + this.code = code; + } + + public String getCode() { + + return code; + + } + +} +@Entity +public class Article { + @Id + private int id; + private String title; + @Basic + private int priorityValue; + @Transient + private Priority priority; + private Category category; +} + +@Converter(autoApply = true) +public class CategoryConverter implements AttributeConverter { + + @Override + + public String convertToDatabaseColumn(Category category) { + if (category == null) { + return null; + } + return category.getCode(); + } + + @Override + public Category convertToEntityAttribute(String code) { + if (code == null) { + return null; + } + return Stream.of(Category.values()) + .filter(c -> c.getCode().equals(code)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } +} +``` + +Мы установили @Converter(autoApply=true), чтобы JPA автоматически применял логику преобразования ко всем сопоставленным атрибутам типа Category. В противном случае нам пришлось бы поместить аннотацию @Converter непосредственно над полем Category у каждой сущности, где оно имеется. В результате в столбце таблицы будут храниться значения: "S", "M" или "T". + +Как мы видим, мы можем просто установить наши собственные правила преобразования перечислений в соответствующие значения базы данных, если мы используем интерфейс AttributeConverter. Более того, мы можем безопасно добавлять новые значения enum или изменять существующие, не нарушая уже сохраненные данные. Это решение просто в реализации и устраняет все недостатки с @Enumerated(EnumType.ORDINAL), @Enumerated(EnumType.STRING) и методами обратного вызова. +[к оглавлению](#jdbc) + +## Как мапятся даты (до Java 8 и после)? +При работе с датами рекомендуется установить определенный часовой пояс для драйвера JDBC. Таким образом, наше приложение будет независимым от текущего часового пояса системы. + +Другой способ - настроить свойство hibernate.jdbc.time_zone в файле свойств Hibernate, который используется для создания фабрики сессий. Таким образом, мы можем указать часовой пояс один раз для всего приложения. + +__java.sql__ +Hibernate позволяет отображать различные классы даты/времени из Java в таблицах баз данных. Стандарт SQL определяет три типа даты/времени: +1. DATE - Представляет календарную дату путем хранения лет, месяцев и дней. Эквивалентом JDBC является java.sql.Date. +2. TIME - Представляет время дня и хранит часы, минуты и секунды. Эквивалентом JDBC является java.sql.Time. +3. TIMESTAMP - Хранит как DATE, так и TIME плюс наносекунды. Эквивалентом JDBC является java.sql.Timestamp. Поскольку эти типы соответствуют SQL, их сопоставление относительно простое. Мы можем использовать аннотацию @Basic или @Column: +```java +@Entity +public class TemporalValues { +@Basic +private java.sql.Date sqlDate; +@Basic +private java.sql.Time sqlTime; +@Basic +private java.sql.Timestamp sqlTimestamp; +} +``` + Затем мы могли бы установить соответствующие значения следующим образом: + +```java +temporalValues.setSqlDate(java.sql.Date.valueOf("2017-11-15")); +temporalValues.setSqlTime(java.sql.Time.valueOf("15:30:14")); +temporalValues.setSqlTimestamp(java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332")); +``` + +Обратите внимание, что использование типов java.sql для полей сущностей не всегда может быть хорошим выбором. Эти классы специфичны для JDBC и содержат множество устаревших функций. + +Чтобы избежать зависимостей от пакета java.sql, начали использовать классы даты/времени из пакета java.util вместо классов java.sql.Timestamp и java.sql.Time -## Data и Enum значения в БД? -+ @Temporal(value=TemporalType.DATE) для Data до Java 8 версии. После 8 версии дополнительных аннотаций не требуется. +__java.util__ +Точность представления времени составляет одну миллисекунду. Для большинства практических задач этого более чем достаточно, но иногда хочется иметь точность повыше. -+ Для Enum нужно указать аннотацию @Enumerated, которая принимает параметр типа EnumType: -1. EnumType.STRING – это значит, что в базе будет хранится имя этого enum. То есть если мы зададим role = RoleEnum.ADMIN, то в БД в поле role будет хранится значение ADMIN. -2. EnumType.ORDINAL – это значит, что в базе будет хранится ID этого enum. ID – это место расположение в списке перечисления начиная с 0. Например если значение enum равно ADMIN, то в базе будет хранится число 2, а если будет ANONYMOUS, то в базе будет хранится 0. +Поскольку классы в данном API изменяемые (не immutable), использовать их в многопоточной среде нужно с осторожностью. В частности java.util.Date можно признать «эффективно» потоко-безопасным, если вы не вызываете у него устаревшие методы. + +__java.util.Date__ +Тип java.util.Date содержит информацию о дате и времени с точностью до миллисекунд. Но так как классы из этого пакета не имели прямого соответствия типам данных SQL, приходилось использовать над полями java.util.Date аннотацию @Temporal, чтобы дать понять SQL, с каким конкретно типом данных она работает. Для этого у аннотации @Temporal нужно было указать параметр TemporalType, который принимал одно из трёх значений: DATE, TIME или TIMESTAMP, что позволяло указать базе данных с какими конкретными типами данных она работает. +```java +@Basic +@Temporal(TemporalType.DATE) +private java.util.Date utilDate; +@Basic +@Temporal(TemporalType.TIME) +private java.util.Date utilTime; +@Basic +@Temporal(TemporalType.TIMESTAMP) +private java.util.Date utilTimestamp; +``` +Тип java.util.Date имеет точность до миллисекунд, и недостаточно точен для обработки SQL-значения Timestamp, который имеет точность вплоть до наносекунд. Поэтому, когда мы извлекаем сущность из базы данных, неудивительно, что в этом поле мы находим экземпляр java.sql.Timestamp, даже если изначально мы сохранили java.util.Date. Но это не страшно, так как Timestamp наследуется от Date. + +__java.util.Calendar__ +Как и в случае java.util.Date, тип java.util.Calendar может быть сопоставлен с различными типами SQL, поэтому мы должны указать их с помощью @Temporal. Разница лишь в том, что Hibernate не поддерживает отображение (маппинг) Calendar на TIME: +```java +@Basic +@Temporal(TemporalType.DATE) +private java.util.Calendar calendarDate; +@Basic +@Temporal(TemporalType.TIMESTAMP) +private_ java.util.Calendar calendarTimestamp +``` + +__java.time__ +Начиная с Java 8, доступен новый API даты и времени для работы с временными значениями. Этот API-интерфейс устраняет многие проблемы классов java.util.Date и java.util.Calendar. Все классы в новом API неизменяемые (immutable) и, как следствие, потоко-безопасные. Точность представления времени составляет одну наносекунду, что в миллион раз точнее чем в пакете java.util. Типы данных из пакета java.time напрямую отображаются (маппятся) на соответствующие типы SQL. Поэтому нет необходимости явно указывать аннотацию @Temporal: +1. LocalDate соответствует DATE. +2. LocalTime и OffsetTime соответствуют TIME. +3. Instant, LocalDateTime, OffsetDateTime и ZonedDateTime соответствуют + TIMESTAMP. + +Это означает, что мы можем пометить эти поля только аннотацией @Basic (или @Column), например: + +```java +@Basic +private java.time.LocalDate localDate; +@Basic +private java.time.LocalTime localTime; +@Basic +private java.time.OffsetTime offsetTime; +@Basic +private java.time.Instant instant; +@Basic +private java.time.LocalDateTime localDateTime; +@Basic +private java.time.OffsetDateTime offsetDateTime; +@Basic +private java.time.ZonedDateTime zonedDateTime; +``` + +Каждый временной класс в пакете java.time имеет статический метод parse() для анализа предоставленного значения типа String с использованием соответствующего формата. Итак, вот как мы можем установить значения полей сущности: +```java +temporalValues.setLocalDate(LocalDate.parse("2017-11-15")); +temporalValues.setLocalTime(LocalTime.parse("15:30:18")); +temporalValues.setOffsetTime(OffsetTime.parse("08:22:12+01:00")); +temporalValues.setInstant(Instant.parse("2017-11-15T08:22:12Z")); +temporalValues.setLocalDateTime( + LocalDateTime.parse("2017-11-15T08:22:12")); +temporalValues.setOffsetDateTime( + OffsetDateTime.parse("2017-11-15T08:22:12+01:00")); +temporalValues.setZonedDateTime( + ZonedDateTime.parse("2017-11-15T08:22:12+01:00[Europe/Paris]")); +``` + +[к оглавлению](#jdbc) ## PersistenceContext Экземпляр EntityManager связан с persistence context. Контекст сохранения-это кэш первого уровня, в котором все сущности извлекаются из базы данных или сохраняются в ней. Он находится между нашим приложением и постоянным хранилищем. Набор экземпляров сущности, в котором для любого персистентного идентификатора сущности существует уникальный экземпляр сущности. В persistence context управляются экземпляры сущностей и их жизненный цикл. API EntityManager используется для создания и удаления постоянных экземпляров сущностей, поиска сущностей по их первичному ключу и выполнения запросов к сущностям. +PersistenceContext доступны в двух типах: PersistenceContext в области транзакций и PersistenceContext расширенной области + ++ Транзакционный контекст персистентности. +Контекст постоянства транзакции привязан к транзакции. Как только транзакция заканчивается, объекты, присутствующие в контексте постоянства, будут сброшены в постоянное хранилище. +Когда мы выполняем какую-либо операцию внутри транзакции, EntityManager проверяет PersistenceContext. Если он существует, он будет использован. В противном случае это создаст PersistenceContext. +Тип контекста персистентности по умолчанию - PersistenceContextType.TRANSACTION. Чтобы указать EntityManager использовать контекст постоянства транзакции, мы просто аннотируем его с помощью @PersistenceContext : +```java +@PersistenceContext +private EntityManager entityManager; +``` + ++ Расширенный персистентный контекст +Расширенный контекст постоянства может охватывать несколько транзакций. Мы можем сохранить сущность без транзакции, но не можем сбросить ее без транзакции. +Чтобы сказать EntityManager использовать контекст персистентности расширенной области, нам нужно применить атрибут type @PersistenceContext: +```java +@PersistenceContext(type = PersistenceContextType.EXTENDED) +private EntityManager entityManager; +``` +https://www.baeldung.com/jpa-hibernate-persistence-context + +[к оглавлению](#jdbc) + ## Entity graph EntityGraph (граф сущностей) - механизм динамического изменения fetchType для каждого запроса. Используя аннотации можно описать то, что будем выбирать из базы. Граф применяется на уровне SQL запроса, таким образом “лишние” данные не выбираются в Java приложение. Но есть одна небольшая проблема: нельзя сказать, какие атрибуты были выбраны, а какие — нет. Для проверки есть API, это делается при помощи класса PersistenceUtil. +```java +@EntityGraph(value = "customer.products") +List findAll(@Nullable Specification specification) +``` + +Для сложных зависимостей в запросах можно использовать NamedEntityGraph, но его нужно явно объявить в самой Entity + +Основная цель JPA Entity Graph - улучшить производительность в рантайме при загрузке базовых полей сущности и связанных сущностей и коллекций. + +Вкратце, Hibernate загружает весь граф в одном SELECT-запросе, то есть все +указанные связи от нужной нам сущности: +```java +@Entity +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String reply; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private Post post; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private User user; +//... +} +@Entity +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String email; +//... +} +@NamedEntityGraph( + name = "post-entity-graph-with-comment-users", + attributeNodes = { + @NamedAttributeNode("subject"), + @NamedAttributeNode("user"), + @NamedAttributeNode(value = "comments", subgraph = "comments-subgraph"), + }, + subgraphs = { + @NamedSubgraph( + name = "comments-subgraph", + attributeNodes = { + @NamedAttributeNode("user") + } + ) + } +) +@Entity +public class Post { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String subject; + @OneToMany(mappedBy = "post") + private List comments = new ArrayList<>(); + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + private User user; + +//... +} +``` + +В данном примере мы создали EntityGraph и при его помощи хотим, чтобы при загрузке сущности Post загружались поля subject, user, comments. Также мы захотели, чтобы у каждой сущности comments загружалось поле user, для чего мы использовали subgraphs. + +EntityGraph можно определить и без помощи аннотаций, а используя entityManager из JPA API: +```java +EntityGraph entityGraph = entityManager.createEntityGraph(Post.class); +entityGraph.addAttributeNodes("subject"); +entityGraph.addAttributeNodes("user"); +entityGraph.addSubgraph("comments").addAttributeNodes("user"); +``` + +JPA определяет два свойства или подсказки, с помощью которых Hibernate может выбирать стратегию извлечения графа сущностей во время выполнения: +- fetchgraph - из базы данных извлекаются только указанные в графе атрибуты. Поскольку мы используем Hibernate, мы можем заметить, что в отличие от спецификаций JPA, атрибуты, статически настроенные как EAGER, также загружаются. + +- loadgraph - в дополнение к указанным в графе атрибутам, также извлекаются атрибуты, статически настроенные как EAGER. В любом случае, первичный ключ и версия, если таковые имеются, всегда загружаются. + +Загрузить EntityGraph можем тремя способами: +1. Используя перегруженный метод find(), который принимает Map с настройкам EntityGraph: +```java + EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph"); + Map properties = new HashMap<>(); + properties.put("javax.persistence.fetchgraph", entityGraph); + Post post = entityManager.find(Post.class, id, properties); +``` + + +2. Используя JPQL и передав подсказку (hint): +```java +EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph-withcomment-users"); + Post post = entityManager.createQuery("select p from Post p where p.id = :id", + Post.class) + .setParameter("id", id) + .setHint("javax.persistence.fetchgraph", entityGraph) + .getSingleResult(); +``` + +3. С помощью Criteria API: +```java +EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph-withcomment-users"); + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Post.class); + Root root = criteriaQuery.from(Post.class); + criteriaQuery.where(criteriaBuilder.equal(root.get("id"), id)); + TypedQuery typedQuery = entityManager.createQuery(criteriaQuery); + typedQuery.setHint("javax.persistence.loadgraph", entityGraph); + Post post = typedQuery.getSingleResult(); +``` + + Все они дают следующий результат: +```java +select + post0_.id as id1_1_0_, + post0_.subject as subject2_1_0_, + post0_.user_id as user_id3_1_0_, + comments1_.post_id as post_id3_0_1_, + comments1_.id as id1_0_1_, + comments1_.id as id1_0_2_, + comments1_.post_id as post_id3_0_2_, + comments1_.reply as reply2_0_2_, + comments1_.user_id as user_id4_0_2_, + user2_.id as id1_2_3_, + user2_.email as email2_2_3_, + user2_.name as name3_2_3_ + from + Post post0_ + left outer join + Comment comments1_ + on post0_.id=comments1_.post_id + left outer join + User user2_ + on post0_.user_id=user2_.id + where + post0_.id=? +``` + + В каждом из них стратегия графа (fetchgraph, loadgraph) указана как подсказка. В первом примере мы использовали Map, в то время как в двух последующих примерах мы использовали метод setHint() + +https://www.baeldung.com/jpa-entity-graph +https://thorben-janssen.com/jpa-21-entity-graph-part-1-named-entity/ + ## Пробелма n+1 select и как ее решить? Есть User и которого есть коллекция машин Cars. Если мы хотим получить список из 10 юзеров, то в бд полетит 11 запросов. 1 запрос на получение самих юзеров и еще 10 запросов на получение списка их машин, отдельно для каждого User. Это приводит к серьёзным проблемам с производительностью. __JOIN FETCH__ -Самое правильное решение - использовать JOIN FETCH и jpql на выборку сущности. Данное решение не поддерживает работу с нативными запросами, но работает любым видом OneToMany/ManyToOne связи. +Самое правильное решение - использовать JOIN FETCH и jpql на выборку сущности. Данное решение не поддерживает работу с нативными запросами, но работает любым видом OneToMany/ManyToOne связи. Может генерировать дополнительные подзапросы, тогда возможно лучше entityGraph брать, чтобы вместо подзапросов join'ы генерировались __FetchMode.SUBSELECT__ -Использовать FetchMode.SUBSELECT и ленивую инициализацию. В этом случае, будет произведено два запроса: первый сделает выборку основной сущности, второй запрос отправится в базу после того, как мы обратимся к ленивому полю. Запрос будет один на получение всех связанных объектов. Данное решение не работает с нативными запросами и с полями, помеченными аннотацией @ManyToOne +Это Аннотация Hibernate, в JPA её нет. Можно использовать только с коллекциями. Будет сделан один sql-запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям-коллекциям, то выполнится еще один запрос для получения связанных коллекций __EntityGraph__ -Использовать entityGraph. Не самое изящное решение для n+1 проблемы. Графы в основном нужны, когда требуется загрузить действительно большой детальный граф, т.е. когда нам нужно получить очень много связанной информации из базы, и такой большой запрос следует оптимизировать. Для n+1 не самое компактное решение, но тоже работает, однако такое решение не подойдет при использовании нативных запросов. + +Не самое изящное решение для n+1 проблемы. Графы в основном нужны, когда требуется загрузить действительно большой детальный граф, т.е. когда нам нужно получить очень много связанной информации из базы, и такой большой запрос следует оптимизировать. Для n+1 не самое компактное решение, но тоже работает, однако такое решение не подойдет при использовании нативных запросов. + +__Batch fetching__ + +Это Аннотация Hibernate, в JPA её нет. Указывается над классом сущности или над полем коллекции с ленивой загрузкой. Будет сделан один sql-запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям-коллекциям, то выполнится еще один запрос для получения связанных коллекций. Изменим пример: +```java +@OneToMany(mappedBy = "customer") +@Fetch(value = FetchMode.SELECT) +@BatchSize(size=5) +private Set orders = new HashSet<>(); +``` + +Например, мы знаем, что в персистентный контекст загружено 12 сущностей Customer, у которых по одному полю-коллекции orders, но так как это @OneToMany, то у них ленивая загрузка по умолчанию и они не загружены в контекст персистентности из БД. При первом обращении к какому-нибудь полю orders, нам бы хотелось, чтобы для всех 12 сущностей Customer были загружены их 12 коллекций Order, по одной для каждой. Но так как у нас @BatchSize(size=5), то Hibernate сделает 3 запроса: в первом и втором получит по пять коллекций, а в третьем получит две коллекции. + +Если мы знаем примерное количество коллекций, которые будут использоваться в любом месте приложения, то можно использовать @BatchSize и указать нужное количество. + +Также аннотация @BatchSize может быть указана у класса. Рассмотрим пример, где у нас есть сущность Order, у которой есть поле типа Product(не коллекция). Мы выгрузили в контекст персистентности 27 объектов Order. При обращении к полям Product у объектов Order будет инициализировано до 10 ленивых прокси сущностей Product одновременно: +```java +@Entity +class Order { +@OneToOne(fetch = FetchType.LAZY) +private Product product; +... +} +@Entity +@BatchSize(size=10) +class Product { +... +} +``` + +Хотя использовать @BatchSize лучше, чем столкнуться с проблемой запроса N+1, в большинстве случаев гораздо лучшей альтернативой является использование DTO или JOIN FETCH, поскольку они позволяют получать все необходимые данные одним запросом. + +__HibernateSpecificMapping, SqlResultSetMapping__ Для нативных запросов рекомендуется использовать именно их. + ## JOIN FETCH vs Join При запросе `JOIN emp.department dep` возвращается только сам запрощенный объект. При запросе `JOIN FETCH emp.department dep` возвращается объект и связанные с ним сущности, в одном запросе. + +## Criteria Api +Не стоит использовать для простых запросов, посколько нужно писать много кода. Хорош при использовании со SpringData для сложных запросов. + +Например нужно создать оч сложный, кастомный фильтр. Создаем класс, имплеметирующий интерфейс Specification, в нем метод toPredicate, который берет наш фильтр, превращает его в предикейт и возвращающет Predicate. А затем посредством SpringData, в дефолтные методы JPARepository например, вставляем эту спецификацию +[к оглавлению](#jdbc) + ## OrderBy vs OrderColumn OrderBy упорядычевает результат запроса (от меньшего к большему и т.д.). -OrderColumn сохраняет порядок в List, используя выделенный столбец данных. Если в листе Юзеров = 1, 2, 3, 4 будет удален Юзер 3, то результат запроса будет 1, 2, Null, 4. Т.е. порядок не нарушится. +Указывает столбец, который используется для поддержания постоянного порядка списка. @OrderColumn указывается в отношении OneToMany или ManyToMany или в коллекции элементов. @OrderColumn указывается на стороне отношения, ссылающейся на коллекцию, которая должна быть упорядочена. OrderColumn сохраняет порядок в List, используя выделенный столбец данных. Если в листе Юзеров = 1, 2, 3, 4 будет удален Юзер 3, то результат запроса будет 1, 2, Null, 4. Т.е. порядок не нарушится. + +@OrderBy в запросе отсортирует, а в кэше вернет неотсортированный порядок. @OrderedColumn сортирует данные с учетом данных в колонке, и в кеше и в запросе. +Указанный порядок @OrderBy применяется только во время выполнения при получении результата запроса. @OrderColumn приводит к постоянному упорядочению соответствующих данных. + +[к оглавлению](#jdbc) ## Как создать составной ключ + Первый метод использования составного ключа включает класс ключа целиком в класс сущности: @EmbeddedId указывает на поле составного первичного ключа, а @Embeddable объявляет класс составным ключом. + Второй вариант использования оставляет поля первичного ключа непосредственно в классе сущности, а класс составного ключа служит лишь для поддержки: @IdClass(Passport.PassportKey.class) +__@IdClass__ +Допустим, у нас есть таблица с именем Account, и она имеет два столбца - accountNumber и accountType, которые формируют составной ключ. Чтобы обозначить оба этих поля как части составного ключа мы должны создать класс, например, AccountId с этими полями: +```java +public class AccountId implements Serializable { +private String accountNumber; +private String accountType; +// default constructor +public AccountId(String accountNumber, String accountType) { +this.accountNumber = accountNumber; +this.accountType = accountType; +} +// equals() and hashCode() +} +``` +Затем нам нужно аннотировать сущность Account аннотацией @IdClass. Мы также должны объявить поля из класса AccountId в сущности Account с такими же именами и аннотировать их с помощью @Id: +```java +@Entity +@IdClass(AccountId.class) +public class Account { +@Id +private String accountNumber; +@Id +private String accountType; +// other fields, getters and setters +} +``` +__@EmbeddedId__ + +Является альтернативой аннотации @IdClass. Рассмотрим другой пример, в котором мы должны сохранить некоторую информацию о книге с заголовком и языком в качестве полей первичного ключа. В этом случае класс первичного ключа, BookId, должен быть аннотирован @Embeddable: +```java +@Embeddable +public class BookId implements Serializable { +private String title; +private String language; +// default constructor +public BookId(String title, String language) { +this.title = title; +this.language = language; +} +// getters, equals() and hashCode() methods +} +//Затем нам нужно встроить этот класс в сущность Book, используя @EmbeddedId: +@Entity +public class Book { +@EmbeddedId +private BookId bookId; +// constructors, other fields, getters and setters +} + +``` +__@IdClass vs @EmbeddedId__ + +- с @IdClass нам пришлось указывать столбцы дважды - в AccountId и в Account. Но с @EmbeddedId мы этого не сделали; +- JPQL-запросы с @IdClass проще. С @EmbeddedId, чтобы получить доступ к полю, нам нужно из сущности обратиться к встраиваемому классу и потом к его полю: +```java +SELECT account.accountNumber FROM Account account // с @IdClass +SELECT book.bookId.title FROM Book book // с @EmbeddedId +``` +- @EmbeddedId более подробна, чем @IdClass, поскольку мы можем получить доступ ко всему объекту первичного ключа, используя метод доступа к полю в классе-сущности. Это также дает четкое представление о полях, которые являются частью составного ключа, поскольку все они агрегированы в классе, который доступен только через метод доступа к полям; +- @IdClass может быть предпочтительным выбором по сравнению с @EmbeddedId в ситуациях, когда класс составного первичного ключа поступает из другого модуля или устаревшего кода, а также когда мы не можем его изменить, например, чтобы установить аннотацию @EmbeddedId. Для таких сценариев, где мы не можем изменить класс составного ключа, аннотация @IdClass является единственным выходом; +- если мы собираемся получить доступ к частям составного ключа по отдельности, мы можем использовать @IdClass, но в тех местах, где мы часто используем полный идентификатор в качестве объекта, @EmbeddedId предпочтительнее. + +[к оглавлению](#jdbc) + ## GeneratedValue Автоматически генерирует значение первичного ключа. Используется 4 стратегии (strategy = GenerationType.IDENTITY), Если мы не указываем значение явно, типом генерации по умолчанию является AUTO. -+ AUTO - значения определяется на основе типа атрибута первичного ключа, -+ IDENTITY - тип генерации основан на IdentityGenerator , который ожидает значения, сгенерированные столбцом identity в базе данных, то есть они автоматически увеличиваются., -+ SEQUENCE - генератор использует последовательности, если они поддерживаются нашей базой данных, и переключается на генерацию таблиц, если они не поддерживаются. Чтобы настроить имя последовательности, мы можем использовать аннотацию @ GenericGenerator со стратегией SequenceStyleGenerator, -+ TABLE - использует базовую таблицу базы данных, которая содержит сегменты значений генерации идентификатора. + +__AUTO__ + +значения определяется на основе типа атрибута первичного ключа. Выбирает стратегию генерации на основе конкретного диалекта базы данных. Для большинства популярных баз данных он выбирает GenerationType.SEQUENCE. +__IDENTITY__ + +Указывает, что для генерации значения первичного ключа будет использоваться столбец IDENTITY, имеющийся в базе данных. Значения в столбце автоматически увеличиваются, что позволяет базе данных генерировать новое значение при каждой операции вставки. С точки зрения базы данных это очень эффективно, поскольку столбцы с автоинкрементом хорошо оптимизированы и не требуют каких-либо дополнительных операторов. Процесс инкремента (получения следующего) первичного ключа происходит вне текущей выполняемой транзакции, поэтому откат транзакции может в конечном итоге обнулить уже присвоенные значения (могут возникнуть пропуски значений). + +Если мы используем Hibernate, то использование IDENTITY имеет существенный nедостаток. Так как Hibernate нужен первичный ключ для работы с managed-объектом в persistence context, а мы не можем узнать значение первичного ключа до выполнения инструкции INSERT, то Hibernate должен немедленно выполнить оператор INSERT, чтобы получить этот самый первичный ключ, сгенерированный БД. Только после этого у Hibernate будет возможность работать с сущностью в контексте персистентности, после чего выполнить операцию persist. Но Hibernate, в соответствии со своей идеологией, использует стратегию “транзакционная запись-после” (transactional writebehind), согласно которой он пытается максимально отложить сброс данных в БД из контекста персистентности, чтобы не делать много обращений к БД. Так как поведение при IDENTITY противоречит идеологии и стратегии “транзакционная запись-после”, Hibernate отключает пакетные вставки (batching inserts) для объектов, использующих генератор IDENTITY. Однако, пакетные обновления и удаления (batching updates и batching deletes) всё же поддерживаются. + +IDENTITY является самым простым в использовании типом генерации, но не самым лучшим с точки зрения производительности. Как уже упоминалось, стратегия генератора первичных ключей IDENTITY не работает при TABLE PER CLASS, поскольку может быть несколько объектов подкласса, имеющих один и тот же идентификатор, и запрос базового класса приведет к получению объектов с одним и тем же идентификатором (даже если они принадлежат разным типам + +__SEQUENCE__ + + Указывает, что для получения значений первичного ключа Hibernate должен использовать имеющиеся в базе данных механизмы генерации последовательных значений (Sequence). Но если наша БД не поддерживает тип SEQUENCE, то Hibernate автоматически переключится на тип TABLE. + +SEQUENCE - это объект базы данных, который генерирует инкрементные целые числа при каждом последующем запросе. SEQUENCE намного более гибкий, чем IDENTITY, потому что: + - SEQUENCE не содержит таблиц, и одну и ту же последовательность можно назначить нескольким столбцам или таблицам; + - SEQUENCE может предварительно распределять значения для улучшения производительности; + - SEQUENCE может определять шаг инкремента, что позволяет нам воспользоваться «объединенным» алгоритмом Hilo; + - SEQUENCE не ограничивает пакетные вставки JDBC в Hibernate; + - SEQUENCE не ограничивает модели наследования Hibernate. + +При SEQUENCE для получения следующего значения из последовательности базы данных требуются дополнительные операторы select, но это не влияет на производительность для большинства приложений. И если нашему приложению необходимо сохранить огромное количество новых сущностей, мы можем использовать некоторые специфичные для Hibernate оптимизации, чтобы уменьшить количество операторов. + + Для работы с этой стратегией Hibernate использует свой класс SequenceStyleGenerator. SEQUENCE - это тип генерации, рекомендуемый документацией Hibernate. + +Самый простой способ - просто задать безымянную генерацию последовательности: +```java +@Entity(name = "Product") + public static class Product { + @Id + @GeneratedValue( + strategy = GenerationType.SEQUENCE // Имя последовательности не + // определено, поэтому Hibernate будет использовать последовательность + // hibernate_sequence для всех сущностей + ) + private Long id; + @Column(name = "product_name") + private String name; + //Getters and setters are omitted for brevity + } +``` + + Для всех сущностей с безымянной последовательностью Hibernate будет использовать одну и ту же hibernate_sequence, из которой будет брать для них айдишники. + +Используя аннотацию @SequenceGenerator, мы можем указать конкретное имя последовательности для таблицы, а также иные параметры. + +Также мы можем настроить под себя несколько разных последовательностей(SEQUENCE-генераторов), указав, например, имя последовательности и начальное значение: +```java +@Entity + public class User { + @Id + @GeneratedValue(generator = "sequence-generator") + @GenericGenerator( + name = "sequence-generator", + strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", + parameters = { + @Parameter(name = "sequence_name", value = "user_sequence"), + @Parameter(name = "initial_value", value = "4"), + @Parameter(name = "increment_size", value = "1") + } + ) + private long userId; + // ... + } +``` + + В этом примере мы установили имя последовательности и начальное значение, что означает, что генерация первичного ключа начнется с 4. Для каждой последовательности сгенерированные значения являются уникальными. + +Так, мы можем назначать разные последовательности разным сущностям и они будут брать айдишники из этой последовательности. В зависимости от требований приложения, можно иметь один генератор на всё приложение, по генератору на каждую сущность или несколько генераторов, которыми пользуются несколько сущностей. + +Например, у нас есть 10 сущностей, для трех из них мы создадим последовательность с именем first_sequence, из которой они будут брать айдишники. Для пяти других сущностей создадим последовательность с именем second_sequence, из которой они будут брать свои айдишники. А для оставшихся двух сущностей можем задать безымянную последовательность, и в этом случае айдишники для них будут браться по умолчанию из hibernate_sequence. + +__TABLE__ + +В настоящее время GenerationType.TABLE используется редко. Hibernate должен получать первичные ключи для сущностей из специальной создаваемой для этих целей таблицы, способной содержать несколько именованных сегментов значений для любого количества сущностей. Основная идея заключается в том, что данная таблица(например, hibernate_sequence) может содержать несколько сегментов со значениями идентификаторов для разных сущностей. Это требует использования пессимистических блокировок, которые помещают все транзакции по получению идентификаторов в очередь. Разумеется, это замедляет работу приложения. + +Третья стратегия, GenerationType.TABLE, не зависит от поддержки конкретной базой данных и хранит счётчики значений в отдельной таблице. С одной стороны это более гибкое и настраиваемое решение, с другой стороны более медленное и требующее большей настройки. Вначале требуется создать (вручную!) и проинициализировать (!) таблицу для значений ключей. Затем создать генератор и связать его со идентификатором: + +Используя аннотацию @TableGenerator мы можем настроить этот тип генерации: +```java +@Entity +public class Department { +@Id +@GeneratedValue(strategy = GenerationType.TABLE, +generator = "table-generator") +@TableGenerator(name = "table-generator", +table = "dep_ids", +pkColumnName = "seq_id", +valueColumnName = "seq_value") +private long depId; +// ... +} +``` Если мы не хотим использовать какие-либо из готовых стратегий, мы можем определить наш собственный генератор, реализуя интерфейс IdentifierGenerator . +GenerationType.IDENTITY - id инкрементится на стороне базы +GenerationType.SEQUENCE - id инкрементится на стороне хибера, использует дополнительные селекты, чтобы запросить id + +[к оглавлению](#jdbc) + ## OrphanRemoval vs cascadeType.remove -+ OrphanRemoval объявляет, что дочерние экземпляры сущностей должны быть удалены, когда на них не ссылаются из родителя. ++ @OrphanRemoval - управляет поведением осиротевшими сущностями. OrphanRemoval объявляет, что дочерние экземпляры сущностей должны быть удалены, когда на них не ссылаются из родителя. + CascadeType.REMOVE: «Дочерняя» сущность удаляется только тогда, когда ее «Родитель» удаляется. Если указан orphanRemoval = true, удаленный экземпляр адреса автоматически удаляется. Если указан только cascade = CascadeType.REMOVE автоматическое действие не выполняется, поскольку удаление отношения не является удалить операцию. -## ElementCollection +[к оглавлению](#jdbc) + +## ElementCollection - Как сохранять в БД коллекции базовых типов? ElementCollection - стандартная аннотация JPA, означает, что коллекция не является совокупностью объектов, а представляет собой набор простых типов (строки и т.д.) или набор встраиваемых элементов (класс, аннотированный с помощью @Embeddable). Это также означает, что элементы полностью принадлежат содержащим объектам: они изменяются, когда объект изменяется, удаляется при удалении объекта и т.д. Они не могут иметь свой собственный жизненный цикл. +@ElementCollection указывается в классе сущности над полем коллекции базовых или встраиваемых типов. Все записи коллекции хранятся в отдельной таблице, то есть в итоге получаем две таблицы: одну для сущности, вторую для коллекции элементов. Конфигурация для таблицы коллекции элементов указывается с помощью аннотации @CollectionTable, которая используется для указания имени таблицы коллекции и JoinColumn, который ссылается на первичную таблицу. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Hibernate7.png) +```java +@Entity +public class Customer { + +@Id + @GeneratedValue + private int id; + private String name; + + @ElementCollection + private List phoneNumbers; + ............. +} + +``` +Аннотация @ElementCollection похожа на отношение @OneToMany, за исключением того, что целью являются базовые и встраиваемые типы, а не сущности. Можно использовать аннотации @AttributeOverrides и @AttributeOverride для настройки отображения в таблице полей базовых или встраиваемых типов. + +Коллекции могут иметь тип java.util.Map, которые состоят из ключа и значения. Для этого типа коллекций применяются следующие правила: +1. Ключ или значение Map может быть базовым типом языка программирования + Java, встраиваемым классом или сущностью. +2. Если значение Map является встраиваемым классом или базовым типом, + используйте аннотацию @ElementCollection. Пример. +3. Если значение Map является сущностью, используйте аннотацию @OneToMany + или @ManyToMany. Пример. +4. Использовать тип Map только на одной стороне двунаправленной связи. + +Аннотация @MapKeyColumn позволяет настроить столбец «ключ» в таблице Map. Аннотация @Column позволяет настроить столбец «значение» в таблице Map. + +Использование коллекций элементов имеет один большой недостаток:элементы коллекции не имеют идентификатора, и Hibernate не может обращаться индивидуально к каждому элементу коллекции. Когда мы добавляем новый объект в коллекцию или удаляем из коллекции существующий элемент, Hibernate удаляет все строки из таблицы элементов и вставляет новые строки по одной для каждого элемента в коллекции. То есть при добавлении одного элемента в коллекцио Hibernate не добавит одну строку в таблицу коллекции, а очистит её и заполнит по новой всеми элементами. + +Поэтому коллекции элементов следует использовать только для очень маленьких коллекций, чтобы Hibernate не выполнял слишком много операторов SQL. Во всех других случаях рекомендуется использовать коллекции сущностей с @OneToMany. + +[к оглавлению](#jdbc) + +## Метод unWrap() +JPA обеспечивает легкий доступ к API базовых реализаций. EntityManager и EntityManagerFactory обеспечивают разворачивают метод, который возвращает соответствующие классы реализации JPA. В случае Hibernate это Session и SessionFactory. + +`Session session = em.unwrap(Session.class); +SessionFactory sessionFactory = em.getEntityManagerFactory().unwrap(SessionFactory.class);` + +В первой строке я получаю текущий сеанс Hibernate от EntityManager . Поэтому я называю UnWrap метод на EntityManager и обеспечить сеанс класса в качестве параметра. +Вторая строка выглядит очень похоже. Я получаю EntityManagerFactory для текущего EntityManager и вызываю метод unwrap специфичный для Hibernate класс SessionFactory. +Эти классы предоставляют вам полный доступ к проприетарным функциям Hibernate, таким как поддержка Streams и Optional. +https://thoughts-on-java.org/hibernate-tips-access-hibernate-apis-jpa/ + +[к оглавлению](#jdbc) # Источники + [Википедия - JDBC](https://ru.wikipedia.org/wiki/Java_Database_Connectivity) diff --git a/kotlin.md b/kotlin.md new file mode 100644 index 0000000..fbbba89 --- /dev/null +++ b/kotlin.md @@ -0,0 +1,3 @@ +[Вопросы для собеседования](README.md) + +# Kotlin diff --git a/oop.md b/oop.md index b6cf8f5..6c48481 100644 --- a/oop.md +++ b/oop.md @@ -1,6 +1,6 @@ [Вопросы для собеседования](README.md) -# ООП +# ООП + Микросервисы + [Что такое _ООП_?](#Что-такое-ООП) + [Назовите основные принципы _ООП_.](#Назовите-основные-принципы-ООП) + [Что такое _«инкапсуляция»_?](#Что-такое-инкапсуляция) @@ -14,6 +14,13 @@ + [В чем разница между _композицией_ и _агрегацией_?](#В-чем-разница-между-композицией-и-агрегацией) + [Что такое _статическое_ и _динамическое связывание_?](#Что-такое-статическое-и-динамическое-связывание) + [Принцип SOLID?](#Принцип-SOLID) ++ [Что такое Монолит, Микросервис?](#Что-такое-Монолит,-Микросервис) ++ [Что такое Continuous Integration, Continuous Delivery/Deployment?](#Что-такое-Continuous-Integration,-Continuous-Delivery/Deployment) ++ [Что выбрать Монолит или Микросервис?](#Что-выбрать-Монолит-или-Микросервис) ++ [Взаимодействие между микросервисами](#Взаимодействие-между-микросервисами) ++ [OpenAPI](#OpenAPI) ++ [Тестирование микросервисов](#Тестирование-микросервисов) ++ [Распределенная трассировка](#Распределенная-трассировка) ## Что такое _ООП_? __Объектно-ориентированное программирование (ООП)__ — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования. @@ -33,7 +40,7 @@ __Объектно-ориентированное программировани + _Наследование_ - создание новой сущности на базе уже существующей. + _Полиморфизм_ - возможность иметь разные формы для одной и той же сущности. + _Абстракция_ - набор общих характеристик. -+ _Посылка сообщений_ - форма связи, взаимодействия между сущностями. ++ _Пересылка сообщений_ - форма связи, взаимодействия между сущностями. + _Переиспользование_- все что перечислено выше работает на повторное использование кода. Это единственно верный порядок парадигм ООП, так как каждая последующая использует предыдущие. @@ -55,11 +62,13 @@ __Наследование__ – это свойство системы, поз [к оглавлению](#ООП) ## Что такое _«полиморфизм»_? -__Полиморфизм__ – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. +__Полиморфизм__ – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Т.е. использовать разные объекты одинаково, через общий интерфейс. + +Пример: у нас есть метод draw(). Для круга он рисует окружность, для квадрата — квадрат. Но мы обращаемся к ним одинаково: figure.draw(). Преимуществом полиморфизма является то, что он помогает снижать сложность программ, разрешая использование одного и того же интерфейса для задания единого набора действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор языка программирования. Отсюда следует ключевая особенность полиморфизма - использование объекта производного класса, вместо объекта базового (потомки могут изменять родительское поведение, даже если обращение к ним будет производиться по ссылке родительского типа). -Полиморфизм бывает _динамическим_ (переопределение) и _статическим_ (перегрузка). +Полиморфизм бывает _динамическим_ (переопределение методов в потомках) и _статическим_ (перегрузка методов -одинаковое имя, разные параметры). _Полиморфная переменная_, это переменная, которая может принимать значения разных типов, а _полиморфная функция_, это функция у которой хотя бы один аргумент является полиморфной переменной. Выделяют два вида полиморфных функций: @@ -72,15 +81,19 @@ _Полиморфная переменная_, это переменная, ко ## Что такое _«абстракция»_? _Абстрагирование_ – это способ выделить набор общих характеристик объекта, исключая из рассмотрения частные и незначимые. Соответственно, __абстракция__ – это набор всех таких характеристик. +Пример: когда мы говорим «машина», нам важно, что у неё есть двигатель и колёса, а цвет ковриков внутри — неважно. + [к оглавлению](#ООП) ## Что представляет собой _«обмен сообщениями»_? -Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это запрос на выполнение действия, дополненный набором аргументов, которые могут понадобиться при выполнении действия. В ООП посылка сообщения (вызов метода) — это единственный путь передать управление объекту. Если объект должен «отвечать» на это сообщение, то у него должна иметься соответствующий данному сообщению метод. Так же объекты, используя свои методы, могут и сами посылать сообщения другим объектам. Обмен сообщениями реализуется с помощью динамических вызовов, что приводит к чрезвычайно позднему связыванию (extreme late binding). +Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это запрос на выполнение действия + данные. + +В ООП посылка сообщения (вызов метода) — это единственный путь передать управление объекту. Если объект должен «отвечать» на это сообщение, то у него должна иметься соответствующий данному сообщению метод. Так же объекты, используя свои методы, могут и сами посылать сообщения другим объектам. [к оглавлению](#ООП) ## Расскажите про основные понятия ООП: _«класс»_, _«объект»_, _«интерфейс»_. -__Класс__ – это способ описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт). +__Класс__ – шаблон описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт). С точки зрения программирования класс можно рассматривать как набор данных (полей, атрибутов, членов класса) и функций для работы с ними (методов). @@ -93,69 +106,63 @@ __Интерфейс__ – это набор методов класса, дос [к оглавлению](#ООП) ## В чем заключаются преимущества и недостатки объектно-ориентированного подхода в программировании? -Преимущества: - -+ Объектная модель вполне естественна, поскольку в первую очередь ориентирована на человеческое восприятие мира, а не на компьютерную реализацию. -+ Классы позволяют проводить конструирование из полезных компонентов, обладающих простыми инструментами, что позволяет абстрагироваться от деталей реализации. -+ Данные и операции над ними образуют определенную сущность, и они не разносятся по всей программе, как нередко бывает в случае процедурного программирования, а описываются вместе. Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения. -+ Инкапсуляция позволяет привнести свойство модульности, что облегчает распараллеливание выполнения задачи между несколькими исполнителями и обновление версий отдельных компонентов. -+ Возможность создавать расширяемые системы. -+ Использование полиморфизма оказывается полезным при: - + Обработке разнородных структур данных. Программы могут работать, не различая вида объектов, что существенно упрощает код. Новые виды могут быть добавлены в любой момент. - + Изменении поведения во время исполнения. На этапе исполнения один объект может быть заменен другим, что позволяет легко, без изменения кода, адаптировать алгоритм в зависимости от того, какой используется объект. - + Реализации работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов. - + Возможности описать независимые от приложения части предметной области в виде набора универсальных классов, или фреймворка, который в дальнейшем будет расширен за счет добавления частей, специфичных для конкретного приложения. -+ Повторное использование кода: - + Сокращается время на разработку, которое может быть отдано другим задачам. - + Компоненты многоразового использования обычно содержат гораздо меньше ошибок, чем вновь разработанные, ведь они уже не раз подвергались проверке. - + Когда некий компонент используется сразу несколькими клиентами, улучшения, вносимые в его код, одновременно оказывают положительное влияние и на множество работающих с ним программ. - + Если программа опирается на стандартные компоненты, ее структура и пользовательский интерфейс становятся более унифицированными, что облегчает ее понимание и упрощает использование. - -Недостатки: - -+ В сложных иерархиях классов поля и методы обычно наследуются с разных уровней. И не всегда легко определить, какие поля и методы фактически относятся к данному классу. -+ Код для обработки сообщения иногда «размазан» по многим методам (иначе говоря, обработка сообщения требует не одного, а многих методов, которые могут быть описаны в разных классах). -+ Документирование классов - задача более трудная, чем это было в случае процедур и модулей. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. -+ Неэффективность и неэкономное распределения памяти на этапе выполнения (по причине издержек на динамическое связывание и проверки типов на этапе выполнения). -+ Излишняя универсальность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. + +✅ Ближе к человеческому восприятию мира. + +✅ Код разбивается на логичные части. + +✅ Удобнее поддерживать и расширять. + +✅ Код можно переиспользовать. + +✅ Легко добавлять новые объекты и изменять поведение программы. + +❌ Иногда сложно понять, где что наследуется. + +❌ Методы могут быть «размазаны» по разным классам. + +❌ Документировать сложнее. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. + +❌ Дополнительные расходы памяти и производительности. + +❌ Избыточность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. [к оглавлению](#ООП) ## Что подразумевают в плане принципов ООП выражения _«является»_ и _«имеет»_? -__«является»__ подразумевает наследование. -__«имеет»__ подразумевает ассоциацию (агрегацию или композицию). +__«является»__ подразумевает наследование. Класс _cat_ является потомком класса _animal_ +__«имеет»__ агрегация или композиция. Класс _car_ имеет класс _engine_ + + [к оглавлению](#ООП) ## В чем разница между _композицией_ и _агрегацией_? -Ассоциация обозначает связь между объектами. Композиция и агрегация — частные случаи ассоциации «часть-целое». +Ассоциация обозначает связь между объектами. -Агрегация предполагает, что объекты связаны взаимоотношением «part-of» (часть). Композиция более строгий вариант агрегации. Дополнительно к требованию «part-of» накладывается условие, что экземпляр «части» может входить только в одно целое (или никуда не входить), в то время как в случае агрегации экземпляр «части» может входить в несколько целых. +Агрегация — «часть» может существовать отдельно и принадлежать нескольким «целым». (Книга находится в библиотеке, её можно перенести в другую). ->Например, книга состоит из страниц и мы не можем вырвать страницу из книги и вложить в другую книгу. Страницы четко привязаны к конкретной книге, поэтому это композиция. -В тоже время мы можем взять и перенести книгу из одной библиотеки в другую - это уже агрегация. +Композиция — «часть» жёстко привязана к «целому». (Страница принадлежит конкретной книге). [к оглавлению](#ООП) ## Что такое _статическое_ и _динамическое связывание_? -Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен. Присоединение вызова метода к телу метода. Если связывание проводится компилятором (компоновщиком) перед запуском программы, то оно называется _статическим_ или _ранним связыванием (early binding)_. +Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен. + +_Статическим_ или _ранним связыванием (early binding)_ — компилятор заранее знает, какой метод будет вызван. (Напр. перегрузка методов). -В свою очередь, _позднее связывание (late binding)_ это связывание, проводимое непосредственно во время выполнения программы, в зависимости от типа объекта. Позднее связывание также называют _динамическим (dynamic)_ или _связыванием на стадии выполнения (runtime binding)_. В языках, реализующих позднее связывание, должен существовать механизм определения фактического типа объекта во время работы программы, для вызова подходящего метода. Иначе говоря, компилятор не знает тип объекта, но механизм вызова методов определяет его и вызывает соответствующее тело метода. Механизм позднего связывания зависит от конкретного языка, но нетрудно предположить, что для его реализации в объекты должна включаться какая-то дополнительная информация. +_Позднее связывание (late binding) или динамическое_ проводимое непосредственно во время выполнения программы, в зависимости от типа объекта (Напр. переопределение методов в наследниках). Механизм позднего связывания зависит от конкретного языка Итак, фундаментальное различие между статическим и динамическим связыванием в Java состоит в том, что первое происходит рано, во время компиляции на основе типа ссылочной переменной, а второе – позднее, во время выполнения, с использованием конкретных объектов. -Для всех методов Java используется механизм позднего (динамического) связывания, если только метод не был объявлен как `static` или `final` (приватные методы являются `final` по умолчанию). +В Java почти все методы — динамические, кроме `static`, `final` и `private`. Ключевые различия между ранним и поздним связыванием в языке Java: -1) Статическое связывание происходит во время компиляции, а динамическое – во время выполнения. - -2) Поскольку статическое связывание происходит на ранней стадии жизненного цикла программы, его называют ранним связыванием. Аналогично, динамическое связывание называют также поздним связыванием, поскольку оно происходит позже, во время работы программы. +1) Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов. -3) Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов. +2) Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. -4) Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. - -5) В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект. +3) В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект. [к оглавлению](#ООП) @@ -170,7 +177,7 @@ __«имеет»__ подразумевает ассоциацию (агрега __Принцип единственной ответственности (SRP)__
Никогда не должно быть больше одной причины изменить класс. -На каждый объект возлагается одна обязанность, полностью инкапсулированная в класс. Все сервисы класса направлены на обеспечение этой обязанности.Такие классы всегда будет просто изменять, если это понадобится, потому что понятно, за что класс отвечает, а за что — нет. +Класс должен быть ответственен лишь за что-то одно. Если класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются связанными друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой. Представьте себе модуль, который обрабатывает заказы. Если заказ верно сформирован, он сохраняет его в базу данных и высылает письмо для подтверждения заказа. Принцип единственной обязанности подразумевает, что три аспекта этой проблемы на самом деле — три разные обязанности. А значит, должны находиться в разных классах или модулях. Объединение нескольких сущностей, которые могут меняться в разное время и по разным причинам, считается плохим проектным решением. Гораздо лучше разделить модуль на три отдельных, каждый из которых будет выполнять одну единственную функцию. @@ -182,7 +189,7 @@ __Принцип открытости/закрытости (OCP)__
__Принцип подстановки Барбары Лисков (LSP)__
Объекты в программе можно заменить их наследниками без изменения свойств программы. -Это означает, что класс, разработанный путем расширения на основании базового класса, должен переопределять его методы так, чтобы не нарушалась функциональность с точки зрения клиента. То есть, если разработчик расширяет ваш класс и использует его в приложении, он не должен изменять ожидаемое поведение переопределенных методов. +Это означает, что класс, разработанный путем расширения на основании базового класса, должен переопределять его методы так, чтобы не нарушалась функциональность с точки зрения клиента. То есть, если разработчик расширяет ваш класс и использует его в приложении, он не должен изменять ожидаемое поведение переопределенных методов. Если оказывается, что в коде проверяется тип класса, значит принцип подстановки нарушается. __Принцип разделения интерфейса (ISP)__
Клиенты не должны быть вынуждены реализовывать методы, которые они не будут использовать. @@ -192,12 +199,289 @@ __Принцип разделения интерфейса (ISP)__
Рассмотрим пример. Разработчик Алекс создал интерфейс "отчет" и добавил два метода: generateExcel() и generatedPdf(). Теперь клиент А хочет использовать этот интерфейс, но он намерен использовать отчеты только в PDF-формате, а не в Excel. Он должен будет реализовать два метода, один из которых по большому счету не нужен и существует только благодаря Алексу — дизайнеру программного обеспечения. Клиент воспользуется либо другим интерфейсом, либо оставит поле для Excel пустым. Так в чем же решение? Оно состоит в разделении существующего интерфейса на два более мелких. Один — отчет в формате PDF, второй — отчет в формате Excel. Это даст пользователю возможность использовать только необходимый для него функционал. __Принцип инверсии зависимостей (DIP)__
-Зависимости внутри системы строятся на основе абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Программное обеспечение нужно разрабатывать так, чтобы различные модули были автономными и соединялись друг с другом с помощью абстракции. +Зависимости внутри системы строятся на основе абстракций. Объектом зависимости должна быть абстракция, а не что-то конкретное. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Программное обеспечение нужно разрабатывать так, чтобы различные модули были автономными и соединялись друг с другом с помощью абстракции. Классическое применение этого принципа — Spring framework. В рамках Spring framework все модули выполнены в виде отдельных компонентов, которые могут работать вместе. Они настолько автономны, что могут быть быть с такой же легкостью задействованы в других программных модулях помимо Spring framework. Это достигнуто за счет зависимости закрытых и открытых принципов. Все модули предоставляют доступ только к абстракции, которая может использоваться в другом модуле. [к оглавлению](#ООП) +## Что такое Монолит, Микросервис? +Монолитная архитектура - система с одним сервисом, который отвечает за работу со всей предметной областью бизнеса (приложения) + +При начале работы над монолитным проектом Достоинства: + +✅ Быстрая разработка + +✅ Возможность внесения радикальных изменений (например изменить архитетуру, класс-дизайн, схему бд) + +✅ Безболезненое тестирование + +✅ Легкое развертывание + +✅ Простое hardware масштабирование + +С увеличением размера проекта и команды разработки появляются Минусы: + +❌ Медленное внесение изменений + +Команды А и Б работают не пересекаясь. Выкатывается новая версия проекта с багами в коде команды Б. Откатывается на предыдущуюю версию весь код и команды Б и команды А. + +❌ Уязвимая надежность - если падает часть кода, то не работает все приложение. + +❌ Трудности в hardware масштабировании. + +Одна часть требует оперативу, другая ядра. Приходится апгрейдить "вертикально" - покупать больше оперативных планок и процессоры с большим кол-вом ядер для всех сервисов. А не по потребностям + +❌ Дороговизна обновления технологического стека. Например переход на новую версию Java. Чтобы проверить эффективность, нужно переписывать весь проект + +Микросервисная архитектура — система, которая содержит больше одного обособленнго сервиса. Размер значения не имеет. +Обособленный сервис — является независимой единицей развертывания, имеет своё хранилище, свою БД (?), отвечает за свою часть предметной области + +Достоинства: + +✅ Возможность масштабирования разработки + +✅ Дешевизна эксперементов с новым стеком + +✅ Инкрементальное обновление технолошического стека + +✅ Повышенная надежность и отказоустойчивость + +Минусы: +❌ Сложность проектирования архитектуры + +❌ Долгое внесение радикальных изменений + +❌ Нетривиальное тестирование + +❌ Повышенные требования к Continuous Integration/Continuous Delivery (деплою) + +Сложности проектирования Микросервисов ++ Сложно разделить предметную область на части + +Не стоит делить монолит на микросервисы по функциональным возможностям. Это приведет к распределенному монолиту - архитектура, включающая недостатки и того и другого. Монолит нужно разбивать по пренадлежности к какой-то области бизнеса. Отличить одно от другого довольно сложно. Т.е. по принципу единой ответственности. + +Соответствие принципам Low coupling, high cohesion способствует быстрому внесению изменений и легкому тетсированию. + +Сильная связанность high cohesion - части системы, которые изменяются вместе, должны находиться ближе друг к другу + +Слабая связанность low coupling - части системы, которые изменяются параллельно, должны иметь как можно меньше зависимостей друг на друга b внутри сервиса и между сервисами + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP1.png) + ++ Задержки при межсервисном взаимодействии влияют на производительность + сеть ненадежна ++ Межсервисное взаимодействие влияет на доступность ++ Сложно добиться согласованности данных + +[к оглавлению](#ООП) + +## Что такое Continuous Integration, Continuous Delivery/Deployment? + +Непрерывная интеграция (CI) — процесс автоматического принятия изменений. Первичный процесс обновления ПО, в рамках которого все изменения на уровне кода вносятся в единый центральный репозиторий. Такое внесение принято называть слиянием. После каждого слияния (которое проходит по несколько раз в день) в изменяемой системе происходит автоматическая сборка (часто приложение упаковывается в Docker) и тестирование (проверка конкретных модулей кода, UI, производительности, надёжности API). Таким образом разработчики страхуются от слишком поздних обнаружений проблем в обновлениях. ++ Возможность локальной сборки проекта ++ На каждый коммит в кдаленный репо запускаем сборки проекта в системе (Jenkins, TravisCI) ++ Сломанная сборка запрещает слияние + +Непрерывная доставка (CD) — CI + CD. Автоматизированый процесс доставки изменений до продакшена. Теперь новая версия не только создаётся и тестируется при каждом изменении кода, регистрируемом в репозитории, но и может быть оперативно запущена по одному нажатию кнопки развёртывания. Однако запуск развёртывания всё ещё происходит вручную — ту самую кнопку всё же надо кому-то нажать. Этот метод позволяет выпускать изменения небольшими партиями, которые легко изменить или устранить в случае необходимости. ++ Поддерживаем артефакты стабильными ++ Развертываем по кнопке + +[к оглавлению](#ООП) + +## Что выбрать Монолит или Микросервис? +Микро если: ++ Предметная область хорошо делится на части ++ Много направлений, которые нужно развивать параллельно ++ "Особенности" монолита не устраивают бизнес + +Монолит если: ++ Нужно быстро сделать прототип ++ Вашу задачу можно решить одним сервисом ++ Непонятно, как разделить предметную область ++ Низкие нагрузки, мало разработчиков, нет ресурсов на поддержание микросервисов + +https://microservices.io/ +https://martinfowler.com/ +"microservices patterns" chris richardson +"clean architecture" robert martin + +[к оглавлению](#ООП) + +## Взаимодействие между микросервисами +Межсервисное взаимодействие = сетевое взаимодействие, т.е. система управляется сообщениями ++ Производительность = задержки и пропускная способность ++ Доступность и отказоустойчивость ++ Архитектура + +__Транспорт__ ++ стандарт JSON внутри HTTP (REST, RPC, GraphQL). Простой, множество инструментов, человекочитаемость. Из-за текстового формата большие объемы и нужно оптимизировать + ++ бинарные протоколы: gRPC, Thrift, Avro и т.д. +Выше производительность, оптимизированно представление данных, можно определить схему сообщения из коробки, но меньше инструментов и не читаемы человеком + +Что выбрать? ++ Начинать с HTTP ++ Бинарные не бесплатные ++ Большинство проблем не из-за протокола! ++ Для публичных API всегда HTTP либо несколько реализаций одна из которых HTTP + +__API__ + +Application Programming Interface - интерфейс, определяющий способ и схему взаимодействия с сервисом ++ Remote Procedure Call (RPC) + +Только POST метод. Все параметры в теле запроса. Любой статус !=200, считается ошибкой +Выбирают: Простая реализация, Внутренние API, HTTP только как транспорт ++ Representational State Transfer (REST) + +Архитектурный стиль, в основе которого лежат ресурсы и их идентификация, изменение их состояния через представление. REST != HTTP +Выбирают: Своего рода стандарт, Внутренние API, Публичные API, HTTP как протокол ++ GraphQL + +Язык запросов для API. Например можно задать конкретный набор полей, которые хотим получить в ответе на запрос +Выбирают: Типо-безопасность, API для Web и мобильных клиентов, тут свой инструментарий + +__Проектирвоание__ ++ Клиент-сервер ++ Stateless - сервис не должен хранить состояние в памяти, это важно для масштабирования ++ Кеширование + +__Типы сообщений__ ++ Команды ++ +Императивное управление (звучит как "сделать что-то"), вызывают сильную связанность, зато понятны - названия обычно отражают логику +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP2.png) + ++ События ++ +Декларативное управление (звучит как "что-то произошло"), снижают связанность, менее понятны - не знаем что скрыто в notifyOrderCreated +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP3.png) + +__Виды взаимодействия__ ++ Синхронные - отправляем запрос и блокируемся, ожидая результата. +Просто и понятно, быстрые операции и низкие задержки (ок), низкая пропускная способность (не ок), вызывает низкую доступность (не ок) ++ Ассинхронные - не блокируемся ожидая ответ +Сложнее в реализации, выше задержки (не ок), выше пропускная способность (ок), повышает доступность (ок) + +Short polling - вызываем операцию и получаем ссылку на ресурс с результатом операции. Переодически опрашиваем этот ресурс на предмет текущего статуса операции +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP4.png) + +Пользователь должен ждать как можно меньше, и нужно выполнять как можно меньше запросов на опрос + +Стратегии опроса: ++ Фиксированное время (раз в секунду) - просто, но время запроса может чуть отличаться, что приведет к лишним запросам ++ Прогрессия (геометрическая/алгебраическая) - сокращает кол-во запросов, но возможен вариант когда придется долго ждать ++ На основе статистического распределения - например, знаем, что запрос выполняется 50% за 1.5с, 90% за 3с и 99% за 5с, мы можем распределить запросы во времени + +Callbacks - вызываем операцию и получаем идентификатор операции. Сервис, выполняющий операцию, отправляет уведомление вызвавшему сервису о результате выплонения операции +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP5.png) + +__Способы взаимодействия__ ++ Прямой обмен А->Б - просто, легкая эксплуатация, но сильная связанность сервисов. Хорошо для Команд ++ Обмен через брокеры сообщений А->Брокер->Б - сложнее, позволяет снизить связанность сервисов. Хорошо для Событий +На основе callback: RabbitMQ +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP6.png) + +На основе polling: Kafka +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP7.png) + +Микросервисы могут работать одновеременно с разными диалектами БД. При этом есть __сильная согласованность__, когда изменения сразу видны всем, с момента их применения и __конечная согласованность__ когда изменения будут видно, но не сразу, а доставляются асинхронно + +__Сильная согласованность__ - всегда актуальные данные, но высокие задержки при получении + +В таких случаях используются __Распределенные транзакции__. Но это сложно, дорого и есть не везде. Можно эмулировать распределенную транзакцию с помощью вложенных локальных транзакций +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP8.png) + +При этом возврат ошибки не гарантирует, что запись не была вставленна в БД. А откат первой транзакции не приводит к откату второй. + +Saga паттерн: Для решения нужно разбивать одну большую распределенную транзакцию на последовательность локальных транзакций и предоставить возможность отката всех локальных транзакций. + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP9.png) + +Особенности: нужно уметь восстанавливаться после сбоев любого участка и повторять сагу с определенного шага + отличать финальные ошибки от нефинальных +Хореография + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP10.png) + + + Нет единой точки отказа + + Легко менять кусочек процесса + + Сложно понять процесс целиком и отслеживать ошибки + + Оркестрация + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP11.png) + + + Есть единая точка отказа + + Сложнее вносить изменения в места + + Легко понять процесс и отслеживать ошибки + + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP12.png) + +__Конечная согласованность__ - могут быть неактуальные данные, зато низкие задержки + + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP13.png) + + __Взаимоисключения__ + + Иногда требуется выполнять какой-то процесс эксклюзивно. Например используя распределенный консенсус - алгоритмы, которые сложно и медленно работают. etcd, Consul, Zookeeper. Зато отказоустойчивы + + Альтернатива - red lock, когда БД используется в качестве хранилища блокировок. Блокировка не захватывается, а арендуется на время. Чтобы в случае блокировки и отказа системы блокировка не осталась навсегда Добавляем версию блокировки для контроля ее корректности + ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP14.png) + +[к оглавлению](#ООП) + +## OpenAPI +OpenAPI - спецификация для описания REST API. + +https://habr.com/ru/post/541592/ + +Swagger - это фреймворк для спецификации RESTful API, цель которого - поддерживать документацию в актуальном виде. Его прелесть заключается в том, что он дает возможность не только интерактивно просматривать спецификацию, но и отправлять запросы – так называемый Swagger UI. + ++ Code first - сначала пишем код, потом генерируем спецификацию ++ Schema first - сперва спецификация, затем генерируем по ней код ++ Публикуем спецификацию (отделный артефак или Swagger) ++ Строго следовать этой спецификации + +Документирование БД +Database Schema as a Code - текущая схема должна быть представлена последовательностью патчей, хранимых как код + ++ Возможность восстановить БД на любом окружении ++ Аудит, контроль, валидация ++ Flyway, Liquibase + +[к оглавлению](#ООП) + +## Тестирование микросервисов ++ Компонентные тесты проверяют работу сервисов и их взаимодействие ++ Модульные тесты для тестирования отдельных функций + +Компонентных должно быть больше, чем модульных, т.к. изменяемая среда, много зависимочти между классами + +Тесты должны быть независимыми и уметь работать параллельно. + +Для тестирования API лучше делать реальные HTTP вызовы, валидировать ответ согласно схеме (JSON). RESTAssured как пример инструмента. + +При тестирование взаимодействия с БД на каждый запуск тестов локально поднимается БД. Embedded или TestContainers. Тесты не должны очищать БД или буффер брокера сообщений перед/после своего запуска/завершения + +При тестировании взаимодействия микросервисов на каждый запуск тестов локально HTTP-сервер, создаем моки операций сервисов, выполняем, моки валидируем. WireMock, MockServer + +При тестировании аинхронных процессов стараемся приблизить тестовый сценарий к реальному, запускаем операцию и ждем завершения выполнения. Awaitility + +Для автоматизации развиваем Continuous Integration/Continuous Delivery - помогает быстрее развивать сервисы и доставлять их на production. Но дорого. + +[к оглавлению](#ООП) + +## Распределенная трассировка +При межсервисном взаимодействии сложно отследить весь путь запроса по системе ++ К каждому внешнему запрсос прикрепляем "идентификатор операции" ++ Пробрасываем его по всей системе ++ Добавляем его в логовые сообщения для отслеживания. Elastic APM, OpenTracing + +Для HTTP: Прикрепляем HTTP-заголовок с этим идентификатором - Используем его при обработке запроса - Поллинг делаем с тем же индентификаторо - Обраный вызов с ним же - В брокер сообщений также отправляем этот индентификатор + +Для Саги: Исходный запрос теряется при обработке асинхронных процессов - В задачу на продолжение саги кладем исходный идентификатор операции (в БД) - При возобновлении саги используем этот идентификатор + +Визуализация трасс помогает построить схему взаимодействия между сервисами. Можно это делать атоматически и динамически. Zipkin, Jaeger + +[к оглавлению](#ООП) + # Источники + [DevColibri](http://devcolibri.com/720) + [Хабрахабр](https://habrahabr.ru/post/87119/) diff --git a/spring.md b/spring.md index 1a29bf5..dba386c 100644 --- a/spring.md +++ b/spring.md @@ -9,6 +9,7 @@ + [Жизненный цикл Context](#Жизненный-цикл-context) + [Как завершить работу контекста](#Как-завершить-работу-контекста) + [Bean](#Bean) ++ [Жизненный цикл бинов](#Жизненный-цикл-бинов) + [Как настроить класс как Spring Bean](#Как-настроить-класс-как-spring-bean) + [Статический Bean](#Статический-bean) + [Inversion of Control](#Inversion-of-control) @@ -16,10 +17,12 @@ + [Как реализуется DI в Spring Framework?](#Как-реализуется-di-в-spring-framework) + [Связывание и @Autowired](#Связывание-и-@Autowired) + [MVC](#mvc) ++ [Шаблон проектирования Front Controller](#Шаблон-проектирования-Front-Controller) + [Связывание форм](#Связывание-форм) + [Исключения в Spring MVC](#Исключения-spring-mvc) + [Локализация в приложениях Spring MVC](#Локализация-в-приложениях-spring-mvc) + [Spring Interceptor](#Spring-interceptor) ++ [CommandLineRunner и ApplicationRunner](#CommandLineRunner-и-ApplicationRunner) + [Реактивное программирование](#Реактивное-программирование) + [Паттерны в Spring Framework](#Паттерны-в-spring-framework) + [AOP и составные части](#AOP-и-составные-части) @@ -31,7 +34,10 @@ + [@Profile](#Profile) + [@LookUp](#LookUp) + [@Target и @Retention](#@Target-и-@Retention) ++ [@Resource](#@Resource) ++ [@Inject](#@Inject) + [@Autowired vs @Resource vs @Inject](#@Autowired-vs-resource-vs-inject) ++ [@Conditional](#@Conditional) + [Как управлять транзакциями в Spring](#Как-управлять-транзакциями-в-spring) + [Как Spring работает с DAO](#Как-spring-работает-с-dao) + [Model vs ModelMap vs ModelAndView](#Model-vs-modelMap-vs-modelAndView) @@ -57,6 +63,10 @@ Spring - фреймворк с открытым исходным кодом, п + Spring Data — дополнительный удобный механизм для взаимодействия с сущностями базы данных, организации их в репозитории, извлечение данных, изменение, в каких то случаях для этого будет достаточно объявить интерфейс и метод в нем, без имплементации. Например с использованием JPA. Состоит из JDBC, ORM, OXM, JMS и модуля Transatcions. JDBC обеспечивает абстрактный слой JDBC и избавляет разработчика от необходимости вручную прописывать монотонный код, связанный с соединением с БД. ORM обеспечивает интеграцию с такими популярными ORM, как Hibernate, JDO, JPA и т.д. Модуль OXM отвечает за связь Объект/XML – XMLBeans, JAXB и т.д. Модуль JMS (Java Messaging Service) отвечает за создание, передачу и получение сообщений. Transactions поддерживает управление транзакциями для классов, которые реализуют определённые методы. + Spring Web module (Web, Servlet, Portlet, Struts) - Модуль Web обеспечивает такие функции, как загрузка файлов и т.д. Web-MVC содержит реализацию Spring MVC для веб-приложений. Web-Socket обеспечивает поддержку связи между клиентом и сервером, используя Web-Socket-ы в веб-приложениях. Web-Portlet обеспечивает реализацию MVC с среде портлетов. + Spring MVC framework - реализация паттерна MVC для построения Web-приложений. ++ Spring Integration - обеспечивает легкий обмен сообщениями в приложениях на базе Spring и поддерживает интеграцию с внешними системами через декларативные адаптеры. Эти адаптеры обеспечивают более высокий уровень абстракции по сравнению с поддержкой Spring для удаленного взаимодействия, обмена сообщениями и планирования. Основная цель Spring Integration - предоставить простую модель для построения корпоративных решений по интеграции, сохраняя при этом разделение задач, что важно для создания поддерживаемого, тестируемого кода. ++ Spring Cloud - инструменты для создания сложных топологий для потоковой и пакетной передачи данных. ++ Spring Batch - предоставляет многократно используемые функции, которые необходимы для обработки больших объемов записей, включая ведение журнала / трассировку, управление транзакциями, статистику обработки заданий, перезапуск заданий, пропуск и управление ресурсами. Он также предоставляет более продвинутые технические услуги и функции, которые позволят выполнять пакетные задания чрезвычайно большого объема и с высокой производительностью благодаря методам оптимизации и разделения. Простые и сложные пакетные задания большого объема могут использовать платформу с высокой степенью масштабируемости для обработки значительных объемов информации. ++ Spring Kafka - Проект Spring for Apache Kafka (spring-kafka) применяет основные концепции Spring для разработки решений для обмена сообщениями на основе Kafka. Он предоставляет «шаблон» в качестве высокоуровневой абстракции для отправки сообщений. Он также обеспечивает поддержку управляемых сообщениями POJO с @KafkaListener аннотациями и «контейнером слушателя». Эти библиотеки способствуют использованию инъекций зависимостей и декларативных. Во всех этих случаях вы увидите сходство с поддержкой JMS в Spring Framework и поддержкой RabbitMQ в Spring AMQP. + Spring Security - Фреймворк аутентификации и авторизации: конфигурируемый инструментарий процессов аутентификации и авторизации, поддерживающий много популярных и ставших индустриальными стандартами протоколов, инструментов, практик. + Тестирование - каркас, поддерживающий классы для написания модульных и интеграционных тестов. @@ -93,12 +103,25 @@ __Spring ApplicationContext Container__ ApplicationContext является б + AnnotationConfigApplicationContext — метаданные конфигурируются с помощью аннотаций прямо на классах. ++ WebApplicationContext — для веб-приложений + + GenericGroovyApplicationContext - эта конфигурация работает по сути так же, как и Xml, только с Groovy-файлами. К тому же, GroovyApplicationContext нормально работает и с Xml-файлом. Принимает на вход строку с конфигурацией контекста. Чтением контекста в данном случае занимается класс GroovyBeanDefinitionReader. Groovy — объектно-ориентированный язык программирования разработанный для платформы Java как альтернатива языку Java с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java кодом и библиотеками. Язык может использоваться в любом Java проекте или как скриптовый язык. При этом мы можем указать несколько файлов конфигурации Spring. +Отличия ApplicationContext и BeanFactory +1. ApplicationContext загружает все бины при запуске, а BeanFactory - по требованию. +2. ApplicationContext расширяет BeanFactory и предоставляет функции, которые подходят для корпоративных приложений: + a. поддержка внедрения зависимостей на основе аннотаций; + b. удобный доступ к MessageSource (для использования в интернационализации); + c. публикация ApplicationEvent - для бинов, реализующих интерфейс ApplicationListener, с помощью интерфейса ApplicationEventPublisher; + d. простая интеграция с функциями Spring AOP. +3. ApplicationContext поддерживает автоматическую регистрацию BeanPostProcessor и BeanFactoryPostProcessor. Поэтому всегда желательно использовать ApplicationContext, потому что Spring 2.0 (и выше) интенсивно использует BeanPostProcessor. +4. ApplicationContext поддерживает практически все типы scope для бинов, а BeanFactory поддерживает только два - Singleton и Prototype. +5. В BeanFactory не будут работать транзакции и Spring AOP. Это может привести к путанице, потому что конфигурация с виду будет корректной + ## Жизненный цикл Context + Контейнер создается при запуске приложения + Контейнер считывает конфигурационные данные (парсинг XML, JavaConfig) @@ -112,6 +135,8 @@ Groovy — объектно-ориентированный язык програ + Контейнер закрывается + Вызываются callback methods +https://habr.com/ru/post/222579/ + ## Как завершить работу контекста Если это не веб-приложение, то есть 2 способа: @@ -132,9 +157,15 @@ Groovy — объектно-ориентированный язык програ В Spring Framework существуют такие свойства, определяющие бины: + class - Этот атрибут является обязательным и указывает конкретный класс Java-приложения, который будет использоваться для создания бина. -+ name - Уникальный идентификатор бина. В случае конфигурации с помощью xml-файла, вы можете использовать свойство “id” и/или “name” для идентификации бина. ++ name - Уникальный идентификатор бина. В случае конфигурации с помощью xml-файла, вы можете использовать свойство “id” и/или “name” для идентификации бина. Атрибут name также может принимать массив String, что позволяет использовать несколько имен. Первый элемент массива будет являться именем и уникальным идентификатором бина, а остальные будут его псевдонимами. -+ scope - Это свойство определяет область видимости создаваемых объектов. __singleton__ - Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию); __prototype__ - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос т.е. иметь любое количество экземпляров бина; __request__ - Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext; __session__ - Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext; __web soccet__ - Создаётся один экземпляр бина для определенного сокета. __application__ - Создаётся один экземпляр бина для жизненного цикла бина. Похоже на синглтон, но когда бобы ограничены областью приложения, значения, однажды установленное в applicationScopedBean, будет сохранено для всех последующих запросов, сеансов и даже для другого приложения сервлета, которое будет обращаться к этому Бобу, при условии, что оно выполняется в том же ServletContext. В то время как одноэлементные бобы ограничены только одним контекстом приложения. ++ scope - Это свойство определяет область видимости создаваемых объектов. +- __singleton__ - Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию); +- __prototype__ - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос т.е. иметь любое количество экземпляров бина; +- __request__ - Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext; +- __session__ - Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext; +- __web socket__ - Создаётся один экземпляр бина для определенного сокета. +- __application__ - Создаётся один экземпляр бина для жизненного цикла бина. Похоже на синглтон, но когда бобы ограничены областью приложения, значения, однажды установленное в applicationScopedBean, будет сохранено для всех последующих запросов, сеансов и даже для другого приложения сервлета, которое будет обращаться к этому Бобу, при условии, что оно выполняется в том же ServletContext. В то время как одноэлементные бобы ограничены только одним контекстом приложения. + constructor-arg - Определяет конструктор, использующийся для внедрения зависимости. Более подробно – далее. @@ -148,6 +179,10 @@ Groovy — объектно-ориентированный язык програ + lazy-initialization mode - Режим ленивой инициализации даёт контейнеру IoC команду создавать экземпляр бина при первом запросе, а не при запуске приложения. +Классы, аннотированные @Configuration, проксируются через CGLIB. Классы @Component или обычные классы не проксируются и не перехватывают вызовы методов с аннотациями @Bean, что означает, что вызовы не будут маршрутизироваться через контейнер и каждый раз будет возвращаться новый экземпляр бина. + +CGLIB (Code Generation Library) - Это библиотека инструментария байтов, используемая во многих средах Java, таких как Hibernate или Spring. Инструментарий байт-кода позволяет манипулировать или создавать классы после фазы компиляции программы. + __Жизненный цикл бинов:__ + Загрузка описаний бинов, создание графа зависимостей(между бинами) + Создание и запуск BeanFactoryPostProcessors @@ -167,6 +202,112 @@ __Жизненный цикл бинов:__ Интерфейс BeanPostProcessor имеет всего два метода: postProcessBeforeInitialization и postProcessAfterInitialization +## Жизненный цикл бинов + +1. __Парсирование конфигурации и создание BeanDefinition__ + +Цель первого этапа — это создание всех BeanDefinition. Объекты BeanDefinition — это набор метаданных будущего бина, макет, по которому нужно будет создавать бин в случае необходимости. То есть для каждого бина создается свой объект BeanDefinition, в котором хранится описание того, как создавать и управлять этим конкретным бином. Проще говоря, сколько бинов в программе - столько и объектов BeanDefinition, их описывающих. + +BeanDefinition содержат (среди прочего) следующие метаданные: +- Имя класса с указанием пакета: обычно это фактический класс бина. +- Элементы поведенческой конфигурации бина, которые определяют, как бин должен вести себя в контейнере (scope, обратные вызовы жизненного цикла и т.д.). +- Ссылки на другие bean-компоненты, которые необходимы для его работы. Эти ссылки также называются зависимостями. +- Другие параметры конфигурации для установки во вновь созданном объекте - например, ограничение размера пула или количество соединений, используемых в бине, который управляет пулом соединений. + +Эти метаданные преобразуются в набор свойств, которые составляют каждое BeanDefinition. В следующей таблице описаны эти свойства: + +При конфигурации через аннотации с указанием пакета для сканирования или JavaConfig используется класс AnnotationConfigApplicationContext. Регистрируются все классы с @Configuration для дальнейшего парсирования, затем регистрируется специальный BeanFactoryPostProcessor, а именно BeanDefinitionRegistryPostProcessor, который при помощи класса ConfigurationClassParser парсирует JavaConfig, загружает описания бинов (BeanDefinition), создаёт граф зависимостей (между бинами) и создаёт: +```java +Map beanDefinitionMap = new ConcurrentHashMap<>(256); + +``` +в которой хранятся все описания бинов, обнаруженных в ходе парсинга конфигурации. + +2. __Настройка созданных BeanDefinition__ + +После первого этапа у нас имеется коллекция Map, в которой хранятся BeanDefinition-ы. BeanFactoryPostProcessor-ы на этапе создания BeanDefinition-ов могут их настроить как нам необходимо. BeanFactoryPostProcessor-ы могут даже настроить саму BeanFactory ещё до того, как она начнет работу по созданию бинов. В интерфейсе BeanFactoryPostProcessor всего один метод: +```java +public interface BeanFactoryPostProcessor { +void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; +} +``` + +3. __Создание кастомных FactoryBean (только для XML-конфигурации)__ + +4. __Создание экземпляров бинов__ + +Сначала BeanFactory из коллекции Map с объектами BeanDefinition достаёт те из них, из которых создаёт все BeanPostProcessor-ы, необходимые для настройки обычных бинов. Создаются экземпляры бинов через BeanFactory на основе ранее созданных BeanDefinition + +5. __Настройка созданных бинов__ + +На данном этапе бины уже созданы, мы можем лишь их донастроить. + +Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки наших бинов до того, как они попадут в контейнер. ApplicationContext автоматически обнаруживает любые бины с реализацией BeanPostProcessor и помечает их как “post-processors” для того, чтобы создать их определенным способом. Например, в Spring есть реализации BeanPostProcessor-ов, которые обрабатывают аннотации @Autowired, @Inject, @Value и @Resource. + +Интерфейс несет в себе два метода: postProcessBeforeInitialization(Object bean, String beanName) и postProcessAfterInitialization(Object bean, String beanName). У обоих методов параметры абсолютно одинаковые. Разница только в порядке их вызова. Первый вызывается до init-метода, второй - после. + +Как правило, BeanPostProcessor-ы, которые заполняют бины через маркерные интерфейсы или тому подобное, реализовывают метод postProcessBeforeInitialization (Object bean, String beanName), тогда как BeanPostProcessor-ы, которые оборачивают бины в прокси, обычно реализуют postProcessAfterInitialization (Object bean, String beanName). + +Прокси — это класс-декорация над бином. Например, мы хотим добавить логику нашему бину, но джава-код уже скомпилирован, поэтому нам нужно на лету сгенерировать новый класс. Этим классом мы должны заменить оригинальный класс так, чтобы никто не заметил подмены. + +Есть два варианта создания этого класса: +- либо он должен наследоваться от оригинального класса (CGLIB) и переопределять его методы, добавляя нужную логику; +- либо он должен имплементировать те же самые интерфейсы, что и первый класс(Dynamic Proxy). + +По конвенции спринга, если какой-то из BeanPostProcessor-ов меняет что-то в классе, то он должен это делать на этапе postProcessAfterInitialization(). Таким образом мы уверены, что initMethod у данного бина, работает на оригинальный метод, до того, как на него накрутился прокси. + +Хронология событий: +1. Сначала сработает метод postProcessBeforeInitialization() всех имеющихся BeanPostProcessor-ов. +2. Затем, при наличии, будет вызван метод, аннотированный @PostConstruct. +3. Если бин имплементирует InitializingBean, то Spring вызовет метод afterPropertiesSet() - не рекомендуется к использованию как устаревший. +4. При наличии, будет вызван метод, указанный в параметре initMethod аннотации @Bean. +5. В конце бины пройдут через postProcessAfterInitialization (Object bean, String beanName). Именно на данном этапе создаются прокси стандартными BeanPostProcessor-ами. Затем отработают наши кастомные BeanPostProcessor-ы и применят нашу логику к прокси-объектам. После чего все бины окажутся в контейнере, который будет обязательно обновлен методом refresh(). +6. Но даже после этого мы можем донастроить наши бины ApplicationListener-ами. +7. Теперь всё + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring1.png) + +6. __Бины готовы к использованию__ + +Их можно получить с помощью метода ApplicationContext#getBean(). + +8. __Закрытие контекста__ + +Когда контекст закрывается (метод close() из ApplicationContext), бин уничтожается. + +Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод. + +Если бин имплементирует DisposibleBean, то Spring вызовет метод destroy() - не рекомендуется к использованию как устаревший. + +Если в аннотации @Bean определен метод destroyMethod, то будет вызван и он. + +__@PostConstruct__ + +Spring вызывает методы, аннотированные @PostConstruct, только один раз, сразу после инициализации свойств компонента. За данную аннотацию отвечает один из BeanPostProcessor-ов. + +Метод, аннотированный @PostConstruct, может иметь любой уровень доступа, может иметь любой тип возвращаемого значения (хотя тип возвращаемого значения игнорируется Spring-ом), метод не должен принимать аргументы. Он также может быть статическим, но преимуществ такого использования метода нет, т.к. доступ у него будет только к статическим полям/методам бина, и в таком случае смысл его использования для настройки бина пропадает. + +Одним из примеров использования @PostConstruct является заполнение базы данных. Например, во время разработки нам может потребоваться создать пользователей по умолчанию. + +__@PreDestroy__ + +Метод, аннотированный @PreDestroy, запускается только один раз, непосредственно перед тем, как Spring удаляет наш компонент из контекста приложения. + +Как и в случае с @PostConstruct, методы, аннотированные @PreDestroy, могут иметь любой уровень доступа, но не могут быть статическими. + +Целью этого метода может быть освобождение ресурсов или выполнение любых других задач очистки до уничтожения бина, например, закрытие соединения с базой данных. + +Обратите внимание, что аннотации @PostConstruct и @PreDestroy являются частью Java EE, а именно пакета javax.annotation модуля java.xml.ws.annotation. И поскольку Java EE устарела в Java 9, то с этой версии пакет считается устаревшим (Deprecated). С Java 11 данный пакет вообще удален, поэтому мы должны добавить дополнительную зависимость для использования этих аннотаций: +```java + +javax.annotation +javax.annotation-api +1.3.2 + +``` + + + ## Как настроить класс как Spring Bean 1) XML конфигурация ```java @@ -196,6 +337,24 @@ MyService service = ctx.getBean(MyService.class); ## Статический Bean Если в классе будет статический метод, то при инициализации впервую очередь создастся статический метод (из-за особенностей статических полей), а потом уже Bean, который "навешивается" на статический метод. +При этом Spring не позволяет внедрять бины напрямую в статические поля, нужно создать нестатический сеттер-метод +```java +@Component +public class TestDataInit { + @Autowired + private static OrderItemService orderItemService; //будет null +} + +@Component +public class TestDataInit { + private static OrderItemService orderItemService; + @Autowired + public void setOrderItemService(OrderItemService orderItemService) { + TestDataInit.orderItemService = orderItemService; + } +} + +``` [к оглавлению](#spring) ## Inversion of Control @@ -203,6 +362,12 @@ MyService service = ctx.getBean(MyService.class); Объекты, создаваемые контейнером, также называются управляемыми объектами (beans). Обычно, конфигурирование контейнера, осуществляется путём внедрения аннотаций (начиная с 5 версии J2SE), но также, есть возможность, по старинке, загрузить XML-файлы, содержащие определение bean’ов и предоставляющие информацию, необходимую для создания bean’ов. +Плюсы такого подхода: +- отделение выполнения задачи от ее реализации; +- легкое переключение между различными реализациями; +- большая модульность программы; +- более легкое тестирование программы путем изоляции компонента или проверки его зависимостей и обеспечения взаимодействия компонентов через контракты. + Объекты могут быть получены одним из двух способов: __Dependency Lookup Поиск зависимости__ — шаблон проектирования, в котором вызывающий объект запрашивает у объекта-контейнера экземпляр объекта с определённым именем или определённого типа. @@ -248,7 +413,7 @@ private Dependency dependency; ``` ## Связывание и @Autowired -Процесс внедрения зависимостей в бины при инициализации называется Spring Bean Wiring. Считается хорошей практикой задавать явные связи между зависимостями, но в Spring предусмотрен дополнительный механизм связывания @Autowired. Аннотация может использоваться над полем или методом для связывания по типу. Чтобы аннотация заработала, необходимо указать небольшие настройки в конфигурационном файле спринг с помощью элемента . +Процесс внедрения зависимостей в бины при инициализации называется Spring Bean Wiring. Считается хорошей практикой задавать явные связи между зависимостями, но в Spring предусмотрен дополнительный механизм связывания @Autowired. Аннотация может использоваться над конструктор, поле, сеттер-метод или метод конфигурации для связывания по типу. Если в контейнере не будет обнаружен необходимый для вставки бин, то будет выброшено исключение, либо можно указать @Autowired(required = false), означающее, что внедрение зависимости в данном месте не обязательно. Чтобы аннотация заработала, необходимо указать небольшие настройки в конфигурационном файле спринг с помощью элемента . Типы связывания: + autowire byName, @@ -256,6 +421,34 @@ private Dependency dependency; + autowire by constructor, + autowiring by @Autowired and @Qualifier annotations +Начиная со Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если целевой компонент определяет только один конструктор. Однако, если доступно несколько конструкторов и нет основного/стандартного конструктора, по крайней мере один из конструкторов должен быть аннотирован @Autowired, чтобы указать контейнеру, какой из них использовать. + +Мы также можем указать Spring предоставить все бины определенного типа из ApplicationContext, добавив аннотацию @Autowired в поле или метод с массивом или коллекцией этого типа, как показано в следующем примере: +```java +@Autowired +private MovieCatalog[] movieCatalogs; +или: +@Autowired +private Set movieCatalogs; +или: +@Autowired +public void setMovieCatalogs(Set movieCatalogs) { +this.movieCatalogs = movieCatalogs; +} +``` + +Даже коллекции типа Map могут быть подключены автоматически, если тип ключа - String. Ключами будут имена бинов, а значениями - сами бины, как показано в следующем примере: +```java +public class MovieRecommender { +private Map movieCatalogs; +@Autowired +public void setMovieCatalogs(Map movieCatalogs){ + this.movieCatalogs = movieCatalogs; +} +// ... +} +``` + [к оглавлению](#spring) ## MVC @@ -293,6 +486,149 @@ Spring MVC предоставляет разработчику следующи + Высокий уровень абстракции для веб-приложений. + В веб-приложениях можно использовать различные части Spring, а не только Spring MVC. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring2.png) + +## Шаблон проектирования Front Controller + +Паттерн Front Controller обеспечивает единую точку входа для всех входящих запросов. Все запросы обрабатываются одним фрагментом кода, который затем может делегировать ответственность за обработку запроса другим объектам приложения. Он также обеспечивает интерфейс для общего поведения, такого как безопасность, интернационализация и передача определенных представлений определенным пользователям. + +В Spring в качестве Front Controller выступает DispatcherServlet, все действия проходят через него. Как правило в приложении задаётся только один DispatcherServlet с маппингом “/”, который перехватывает все запросы. Это и есть реализация паттерна Front Controller. + +Однако иногда необходимо определить два и более DispatcherServlet-а, которые будут отвечать за свой собственный функционал. Например, чтобы один обрабатывал REST-запросы с маппингом “/api”, а другой обычные запросы с маппингом “/default”. Spring предоставляет нам такую возможность, и для начала нужно понять, что: + +- Spring может иметь несколько контекстов одновременно. Одним из них будет корневой контекст, а все остальные контексты будут дочерними. +- Все дочерние контексты могут получить доступ к бинам, определенным в корневом контексте, но не наоборот. Корневой контекст не может получить доступ к бинам дочерних контекстов. +- Каждый дочерний контекст внутри себя может переопределить бины из корневого контекста. + +Каждый DispatcherServlet имеет свой дочерний контекст приложения. DispatcherServlet по сути является сервлетом(он расширяет HttpServlet), основной целью которого является обработка входящих вебзапросов, соответствующих настроенному шаблону URL. Он принимает входящий URI и находит правильную комбинацию контроллера и вида. Веб-приложение может определять любое количество DispatcherServlet-ов. Каждый из них будет работать в своем собственном пространстве имен, загружая свой собственный дочерний WebApplicationContext (на рисунке - Servlet WebApplicationContext) с вьюшками, контроллерами и т.д. Например, когда нам нужно в одном Servlet WebApplicationContext определить обычные контроллеры, а в другом REST-контроллеры. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring3.png) + +WebApplicationContext расширяет ApplicationContext (создаёт и управляет бинами и т.д.), но помимо этого он имеет дополнительный метод getServletContext(), через который у него есть возможность получать доступ к ServletContext-у. + +ContextLoaderListener создает корневой контекст приложения (на рисунке - Root WebApplicationContext) и будет использоваться всеми дочерними контекстами, созданными всеми DispatcherServlet. Напомню, что корневой контекст приложения будет общим и может быть только один. Root WebApplicationContext содержит компоненты, которые видны всем дочерним контекстам, такие как сервисы, репозитории, компоненты инфраструктуры и т.д. После создания корневого контекста приложения он сохраняется в ServletContext как атрибут, имя которого: +```java +WebApplicationContext.class.getName() + ".ROOT" +``` +Чтобы из контроллера любого дочернего контекста обратиться к корневому контексту приложения, мы можем использовать класс WebApplicationContextUtils, содержащий статические методы: +```java +@Autowired +ServletContext context; +ApplicationContext ac =WebApplicationContextUtils.getWebApplicationContext(context); +if(ac == null){ + return "root application context is null"; +} +``` +__ContextLoaderListener vs DispatcherServlet__ + +1. ContextLoaderListener создает корневой контекст приложения. +2. Каждый DispatcherServlet создаёт себе один дочерний контекст. +3. Дочерние контексты могут обращаться к бинам, определенным в корневом контексте. +4. Бины в корневом контексте не могут получить доступ к бинам в дочерних контекстах (напрямую). +5. Все контексты добавляются в ServletContext. +6. Мы можем получить доступ к корневому контексту, используя класс WebApplicationContextUtils. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring4.png) + +## В чем разница между Filters, Listeners и Interceptors? + +__Filter__ + +Это интерфейс из пакета javax.servlet, имплементации которого выполняют задачи фильтрации либо по пути запроса к ресурсу (сервлету, либо по статическому контенту), либо по пути ответа от ресурса, либо в обоих направлениях. + +Фильтры выполняют фильтрацию в методе doFilter. Каждый фильтр имеет доступ к объекту FilterConfig, из которого он может получить параметры инициализации, и ссылку на ServletContext, который он может использовать, например, для загрузки ресурсов, необходимых для задач фильтрации. Фильтры настраиваются в дескрипторе развертывания веб-приложения. + +В веб-приложении мы можем написать несколько фильтров, которые вместе называются цепочкой фильтров. Веб-сервер решает, какой фильтр вызывать первым, в соответствии с порядком регистрации фильтров. + +Когда вызывается метод doFilter(ServletRequest request, ServletResponse response, FilterChain chain) первого фильтра, веб-сервер создает объект FilterChain, представляющий цепочку фильтров, и передаёт её в метод. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring5.png) + +__Interceptor__ + +Это интерфейс из пакета org.aopalliance.intercept, предназначенный для аспектноориентированного программирования. В Spring, когда запрос отправляется в Controller, перед тем как он в него попадёт, он может пройти через перехватчики Interceptor (0 или более). Это одна из реализаций АОП в Spring. Вы можете использовать Interceptor для выполнения таких задач, как запись в Log, добавление или обновление конфигурации перед тем, как запрос обработается Controller-ом. + +Стек перехватчиков: он предназначен для связывания перехватчиков в цепочку в определенном порядке. При доступе к перехваченному методу или полю перехватчик в цепочке перехватчиков вызывается в том порядке, в котором он был определен. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring6.png) + +Мы можем использовать Interceptor-ы для выполнения логики до попадания в контроллер, после обработки в контроллере, а также после формирования представления. Также можем запретить выполнение метода контроллера. Мы можем указать любое количество перехватчиков. + +Перехватчики работают с HandlerMapping и поэтому должны реализовывать интерфейс HandlerInterceptor или наследоваться от готового класса HandlerInterceptorAdapter. В случае реализации HandlerInterceptor нам нужно переопределить 3 метода, а в случае HandlerInterceptor, только необходимые нам: + +- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - вызывается после того, как HandlerMapping определил соответствующий контроллер, но до того, как HandlerAdapter вызовет метод контроллера. С помощью этого метода каждый перехватчик может решить, прервать цепочку выполнения или направить запрос на испольнение дальше по цепочке перехватчиков до метода контроллера. Если этот метод возвращает true, то запрос отправляется следующему перехватчику или в контроллер. Если метод возвращает false, то исполнение запроса прекращается, обычно отправляя ошибку HTTP или записывая собственный ответ в response. + +- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) - отработает после контроллера, но перед формированием представления. Мы можем использовать этот метод для добавления дополнительных атрибутов в ModelAndView или для определения времени, затрачиваемого методом-обработчиком на обработку запроса клиента. Вы можете добавить больше объектов модели в представление, но вы не можете изменить HttpServletResponse, так как он уже зафиксирован. + +- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) - отработает после формирования представления. Вызывается только в том случае, если метод preHandle этого перехватчика успешно завершен и вернул true! + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring7.png) + +Следует знать, что HandlerInterceptor связан с бином DefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, помеченному аннотацией @Controller. + +Чтобы добавить наши перехватчики в конфигурацию Spring, нам нужно переопределить метод addInterceptors () внутри класса, который реализует WebMvcConfigurer: +```java +@Override +public void addInterceptors(InterceptorRegistry registry) { +// LogInterceptor applies to all URLs. +registry.addInterceptor(new LogInterceptor()); +// This interceptor applies to URL /admin/oldLogin. +// Using OldURLInterceptor to redirect to new URL. +registry.addInterceptor(new OldLoginInterceptor()) +.addPathPatterns("/admin/oldLogin"); +// This interceptor applies to URLs like /admin/* +// Exclude /admin/oldLogin +registry.addInterceptor(new AdminInterceptor()) +.addPathPatterns("/admin/*")// +.excludePathPatterns("/admin/oldLogin"); +} +``` + +__Filter vs. Interceptor__ + +- Перехватчик основан на механизме Reflection, а фильтр основан на обратном вызове функции. +- Фильтр зависит от контейнера сервлета, тогда как перехватчик не зависит от него. +- Перехватчики могут работать только с запросами к контроллерам, в то время как фильтры могут работать почти со всеми запросами (например, js, .css и т.д.). +- Перехватчики в отличии от фильтров могут обращаться к объектам в контейнере Spring, что даёт им более изощренный функционал. + +Порядок работы: +1. Фильтры до; +2. Перехватчики до; +3. Метод контроллера; +4. Перехватчики после; +5. Фильтры после. + +HandlerInterceptor в основном похож на Servlet Filter, но в отличие от последнего он просто позволяет настраивать предварительную обработку с возможностью запретить выполнение самого обработчика и настраивать постобработку. + +Согласно документации Spring, фильтры более мощные, например, они позволяют обмениваться объектами запроса и ответа, которые передаются по цепочке. Это означает, что фильтры работают больше в области запроса/ответа, в то время как HandlerInterceptors являются бинами и могут обращаться к другим компонентам в приложении. Обратите внимание, что фильтр настраивается в web.xml, а HandlerInterceptor в контексте приложения. + +__Java Listener__ + +Listener (Слушатель) - это класс, который реализует интерфейс javax.servlet.ServletContextListener. Он инициализируется только один раз при запуске вебприложения и уничтожается при остановке веб-приложения. Слушатель сидит и ждет, когда произойдет указанное событие, затем «перехватывает» событие и запускает собственное событие. Например, мы хотим инициализировать пул соединений с базой данных до запуска веб-приложения. ServletContextListener - это то, что нам нужно, он будет запускать наш код до запуска веб-приложения. + +Все ServletContextListeners уведомляются об инициализации контекста до инициализации любых фильтров или сервлетов в веб-приложении. + +Все ServletContextListeners уведомляются об уничтожении контекста после того, как все сервлеты и фильтры уничтожены. + +Чтобы создать свой Listener нам достаточно создать класс, имплементирующий интерфейс ServletContextListener и поставить над ним аннотацию @WebListener: +```java +@WebListener +public class MyAppServletContextListener +implements ServletContextListener{ +//Run this before web application is started +@Override +public void contextInitialized(ServletContextEvent arg0) { + System.out.println("ServletContextListener started"); +} +@Override +public void contextDestroyed(ServletContextEvent arg0) { + System.out.println("ServletContextListener destroyed"); +} +} +``` + + + ## Связывание форм @ModelAttribute - связывает параметр метода или возвращаемое значение метода с именованным атрибутом модели, а затем возвращает его view веб-представлению. @@ -342,6 +678,27 @@ Spring MVC предоставляет очень простую и удобну [к оглавлению](#spring) +## CommandLineRunner и ApplicationRunner +Эти интрефейсы используются для запуска логики при запуске приложения, после создания экземпляра контекста приложения Spring. + +ApplicationRunner.run() и CommandLineRunner.run() выполнятся сразу после создания applicationcontext и до запуска приложения. Оба они обеспечивают одинаковую функциональность, и единственное различие между CommandLineRunner и ApplicationRunner состоит в том, что CommandLineRunner.run() принимает String array[], тогда как ApplicationRunner.run() принимает ApplicationArguments в качестве аргумента. +```java +@Component +public class CommandLineAppStartupRunner implements CommandLineRunner { + private static final Logger LOG = + LoggerFactory.getLogger(CommandLineAppStartupRunner.class); + + public static int counter; + + @Override + public void run(String...args) throws Exception { + LOG.info("Increment counter"); + counter++; + } +} +``` +Можно запускать несколько CommandLineRunner одновременно, например чтобы распаралелить сложную логику. Управлять их порядком через @Order. Каждый Runner может иметь свои собственные зависимости + ## Реактивное программирование Реактивное программирование — это программирование в многопоточной среде. @@ -398,6 +755,7 @@ View Helper отделяет статическое содержимое в пр + after-returning - Запускает совет после выполнения метода, только в случае его успешного выполнения. + after-throwing - Запускает совет после выполнения метода, только в случае, когда этот метод “бросает” исключение. + around - Запускает совет до и после выполнения метода. +При этом инпоинты видят только начало и конец метода. Например, если метод выполняет транзакцию и где-то в середине кода try/catch поймал exception, транзакция все равно будет свершена, rollback не произойдет. В этом случае нужно пробрасывать ошибку за пределы метода. Срез точек (Pointcut) - Срезом называется несколько объединённых точек (join points), в котором должен быть выполнен совет. @@ -407,6 +765,8 @@ View Helper отделяет статическое содержимое в пр Плетение (Weaving) - Это процесс связывания аспектов с другими объектами приложения для создания совета. Может быть вызван во время компиляции, загрузки или выполнения приложения. +С помощью АОП мы можем прописать, например, что будет выполняться до или после какого-то действия. Прописываем это один раз и этот функционал будет работать везде. Например нам нужно сделать логирование во всех методах @Service, с ООП нам бы пришлось прописывать этот функционал в каждом методе для всех @Service. А с АОП мы можем в конфигах прописать для @Service что будет происходить с каждым вызовом его методов, - в нашем случае писать логи. Элементы АОП такие как аспекты также используются в транзакциях спринга. + ## Spring AOP vs ASPECTJ AspectJ де-факто является стандартом реализации АОП. Реализация АОП от Spring имеет некоторые отличия: + Spring AOP немного проще, т.к. нет необходимости следить за процессом связывания. @@ -423,7 +783,7 @@ AspectJ де-факто является стандартом реализаци + @Sheduler - Таймер. Раз в сколько-то секунд обрабатывать. + @Resource - Java аннотация, которой можно внедрить зависимость. + @Requared - применяется к методам-сеттерам и означает, что значение метода должно быть установлено в XML-файле. Если этого не будет сделано, то мы получим BeanInitializationException. -+ @RequestMapping - позволяет задать шаблон маппинга URI в методе обработчике контроллера. ++ @RequestMapping - используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика. + @ResponseBody - позволяет отправлять Object в ответе. Обычно используется для отправки данных формата XML или JSON. + @ResponseEntity - используется для формирования ответа HTTP с пользовательскими параметрами (заголовки, http-код и т.д.). ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody. + @PathVariable - задает динамический маппинг значений из URI внутри аргументов метода обработчика, т.е. позволяет вводить в URI переменную пути в качестве параметра @@ -432,13 +792,13 @@ AspectJ де-факто является стандартом реализаци + @Scope - указывает scope у spring bean. + @Configuration, @ComponentScan и @Bean - для java based configurations. + AspectJ аннотации для настройки aspects и advices, @Aspect, @Before, @After,@Around, @Pointcut и др. - ++ @PageableDefault - устанавливает значение по умолчанию для параметра разбиения на страницы ## Различия @Component, @Service, @Repository, @Controller Они все служат для обозначения класса как Бин. + @Component - Spring определяет этот класс как кандидата для создания bean. + @Service - класс содержит бизнес-логику и вызывает методы на уровне хранилища. Ничем не отличается от классов с @Component. -+ @Repository - указывает, что класс выполняет роль хранилища (объект доступа к DAO). При этом автоматически перехватывает спецефические Java исключения и пробрасывает их дальше как неконтролируемые исключения доступа к данным Spring. Для этого в контексте прописывается класс PersistenceExceptionTranslationPostProcessor. ++ @Repository - указывает, что класс выполняет роль хранилища (объект доступа к DAO). При этом отлавливает определенные исключения персистентности и пробрасывает их как одно непроверенное исключение Spring Framework. Для этого Spring оборачивает эти классы в прокси, и в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor + @Controller - указывает, что класс выполняет роль контроллера MVC. Диспетчер сервлетов просматривает такие классы для поиска @RequestMapping. ## Различия @Controller и @RestController @@ -450,10 +810,13 @@ AspectJ де-факто является стандартом реализаци Если есть два одинаковых бина (по типу и имени) спринг не знает какой именно использовать и выдаёт exeption. Если над одним из этих бинов установленна @Primary, то его использовать предпочтительнее. Но если нам нужно использовать в работе оба этих бина, можно над каждым поставить @Qualifier и задать имя, для идентификации этих бинов. ## @Profile -Используя аннотацию @Profile - мы сопоставляем bean-компонент с этим конкретным профилем; аннотация просто берет имена одного (или нескольких) профилей. Отвечает за то - какие бины буду создаваться, в зависимости от профайла. +Используя аннотацию @Profile - мы сопоставляем bean-компонент с этим конкретным профилем; аннотация просто берет имена одного (или нескольких) профилей. Отвечает за то - какие бины буду создаваться, в зависимости от профайла. Фактически реализована с помощью гораздо более гибкой аннотации @Conditional. Рассмотрим базовый сценарий - у нас есть компонент, который должен быть активным только во время разработки, но не должен использоваться в производстве. Мы аннотируем этот компонент с профилем «dev», и он будет присутствовать в контейнере только во время разработки - в производственном процессе dev просто не будет активен. +Или можно задать @Profile("postgres") и @Profile("mysql"), а в application.properties указать, бин с каким профилем использовать = spring.profiles.active = mysql + +По умолчанию, если профиль бина не определен, то он относится к профилю “default”. Spring также предоставляет способ установить профиль по умолчанию, когда другой профиль не активен, используя свойство «spring.profiles.default». [к оглавлению](#spring) ## @LookUp @@ -461,8 +824,35 @@ AspectJ де-факто является стандартом реализаци __ПРИМЕР__ - Обычно бины в приложении Spring являтся синглтонами, и для внедрения зависимостей мы используем конструктор или сеттер. Но бывает и другая ситуация: имеется бин Car – синглтон (singleton bean), и ему требуется каждый раз новый экземпляр бина Passenger. То есть Car – синглтон, а Passenger – так называемый прототипный бин (prototype bean). Жизненные циклы бинов разные. Бин Car создается контейнером только раз, а бин Passenger создается каждый раз новый – допустим, это происходит каждый раз при вызове какого-то метода бина Car.Вот здесь то и пригодится внедрение бина с помощью Lookup метода. Оно происходит не при инициализации контейнера, а позднее: каждый раз, когда вызывается метод. - +```java +@Component +public class Car { + @Lookup + public Passenger createPassenger() { + return null; + } + public String drive(String name) { + Passenger passenger = createPassenger(); + passenger.setName(name); + return "car with " + passenger.getName(); + } +} +``` Суть в том, что вы создаете метод-заглушку в бине Car и помечаете его специальным образом – аннотацией @Lookup. Этот метод должен возвращать бин Passenger, каждый раз новый. Контейнер Spring под капотом создаст подкласс и переопределит этот метод и будет вам выдавать новый экземпляр бина Passenger при каждом вызове аннотированного метода. Даже если в вашей заглушке он возвращает null (а так и надо делать, все равно этот метод будет переопределен). +```java +@Component +@Scope("prototype") +public class Passenger { + private String name; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} +``` +Теперь при вызове метода drive() мы можем везти каждый раз нового пассажира. Имя его передаётся в аргументе метода drive(), и затем задается сеттером во вновь созданном экземпляре пассажира. [к оглавлению](#spring) @@ -484,6 +874,64 @@ __ПРИМЕР__ - Обычно бины в приложении Spring явля [к оглавлению](#spring) +## @Resource +Java-аннотация @Resource может применяться к классам, полям и методам. Она пытается получить зависимость: сначала по имени, затем по типу, затем по описанию (Qualifier). Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name. При аннотировании классов имя не извлекается из имени класса по умолчанию, поэтому оно должно быть указано явно. + +Указав данную аннотацию у полей или методов с аргументом name, в контейнере будет произведен поиск компонентов с данным именем, и в контейнере должен быть бин с таким именем: +```java +@Resource(name="namedFile") +private File defaultFile; +``` + +Если указать её без аргументов, то Spring Framework поможет найти бин по типу. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier: +```java +@Resource +@Qualifier("defaultFile") +private File dependency1; +@Resource +@Qualifier("namedFile") +private File dependency2; +``` + +__Разница с @Autowired:__ +- ищет бин сначала по имени, а потом по типу; +- не нужна дополнительная аннотация для указания имени конкретного бина; +- @Autowired позволяет отметить место вставки бина как необязательное @Autowired(required = false); +- при замене Spring Framework на другой фреймворк, менять аннотацию @Resource не нужно + +[к оглавлению](#spring) + +## @Inject +Размещается над полями, методами, и конструкторами с аргументами. @Inject как и @Autowired в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. Это означает, что даже если имя переменной ссылки на класс отличается от имени компонента, но они одинакового типа, зависимость все равно будет разрешена: +```java +@Inject +private ArbitraryDependency fieldInjectDependency; +//fieldInjectDependency - отличается от имени компонента, настроенного в контексте приложения: + +@Bean +public ArbitraryDependency injectDependency() { +ArbitraryDependency injectDependency = new ArbitraryDependency(); +return injectDependency; +} +``` + +Разность имён injectDependency и fieldInjectDependency не имеет значения, зависимость будет подобрана по типу ArbitraryDependency. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier: +```java +@Inject +@Qualifier("defaultFile") +private ArbitraryDependency defaultDependency; + +@Inject +@Qualifier("namedFile") +private ArbitraryDependency namedDependency; + +//При использовании конкретного имени (Id) бина используем @Named: +@Inject +@Named("yetAnotherFieldInjectDependency") +private ArbitraryDependency yetAnotherFieldInjectDependency +``` + + ## @Autowired vs @Resource vs @Inject Аннотации для внедрения зависимостей. @@ -491,11 +939,136 @@ __ПРИМЕР__ - Обычно бины в приложении Spring явля @Inject (java) или @Autowired (spring) в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. +## @Conditional +Часто бывает полезно включить или отключить весь класс @Configuration, @Component или отдельные методы @Bean в зависимости от каких-либо условий. + +Аннотация @Conditional указывает, что компонент имеет право на регистрацию в контексте только тогда, когда все условия соответствуют. Может применяться: +- над классами прямо или косвенно аннотированными @Component, включая классы @Configuration; +- над методами @Bean; +- как мета-аннотация при создании наших собственных аннотаций-условий. + +Условия проверяются непосредственно перед тем, как должно быть зарегистрировано BeanDefinition компонента, и они могут помешать регистрации данного BeanDefinition. Поэтому нельзя допускать, чтобы при проверке условий мы взаимодействовали с бинами (которых еще не существует), с их BeanDefinition-ами можно. + +Условия мы определяем в специально создаваемых нами классах, которые должны имплементировать функциональный интерфейс Condition с одним единственным методом, возвращающим true или false: +```java +boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) +``` +Создав свой класс и переопределив в нем метод matches() с нашей логикой, мы должны передать этот класс в аннотацию @Conditional в качестве параметра: +```java +@Configuration +@Conditional(OurConditionClass.class) +class MySQLAutoconfiguration { +//... +} +//Для того, чтобы проверить несколько условий, можно передать в @Conditional несколько классов с условиями: +@Bean +@Conditional(HibernateCondition.class, OurConditionClass.class) +Properties additionalProperties() { +//... +} +``` + + +Если класс @Configuration помечен как @Conditional, то на все методы @Bean, аннотации @Import и аннотации @ComponentScan, связанные с этим классом, также будут распространяться указанные условия. + +Для более детальной настройки классов, аннотированных @Configuration, предлагается использовать интерфейс ConfigurationCondition. + +В одном классе - одно условие. Для создания более сложных условий можно использовать классы AnyNestedCondition, AllNestedConditions и NoneNestedConditions. + +В Spring Framework имеется множество готовых аннотаций (и связанных с ними склассами-условиями, имплементирующими интерфейс Condition), которые можно применять совместно над одним определением бина: + +__ConditionalOnBean__ Условие выполняется, в случае если присутствует нужный бин в BeanFactory. +__ConditionalOnClass__ Условие выполняется, если нужный класс есть в classpath. +__ConditionalOnCloudPlatform__ Условие выполняется, когда активна определенная платформа. +__ConditionalOnExpression__ Условие выполняется, когда SpEL выражение вернуло положительное значение. +__ConditionalOnJava__ Условие выполняется, когда приложение запущено с определенной версией JVM. +__ConditionalOnJndi__ Условие выполняется, только если через JNDI доступен определенный ресурс. +__ConditionalOnMissingBean__ Условие выполняется, в случае если нужный бин отсутствует в контейнере. +__ConditionalOnMissingClass__ Условие выполняется, если нужный класс отсутствует в classpath. +__ConditionalOnNotWebApplication__ Условие выполняется, если контекст приложения не является веб контекстом. +__ConditionalOnProperty__ Условие выполняется, если в файле настроек заданы нужные параметры. +__ConditionalOnResource__ Условие выполняется, если присутствует нужный ресурс в classpath. +__ConditionalOnSingleCandidate__ Условие выполняется, если bean-компонент указанного класса уже содержится в контейнере и он единственный. +__ConditionalOnWebApplication__ Условие выполняется, если контекст приложения является веб контекстом. + + ## Как управлять транзакциями в Spring Spring поддерживает два типа управления транзакциями: + Программное управление транзакциями: Вы должны управлять транзакциями с помощью программирования. Это способ достаточно гибкий, но его сложно поддерживать. Либо через использование TransactionTemplate, либо через реализацию PlatformTransactionManager напрямую. Используется, если нужно работать с небольшим количеством транзакций. + Декларативное управление транзакциями: Вы отделяете управление транзакциями от бизнес-логики. Вы используете только аннотации @Transactional и конфигурацией на основе XML для управления транзакциями. Наиболее предпочтительный способ. +Простая реализация PlatformTransactionManager это DataSourceTransactionManager, который на каждую транзакцию в БД будет создавать Connection. +```java +DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); //описание транзакции, можно задавать параметры +TransactionStatus status = transactionManager.getTransaction(); //статус транзакции + +try { + fooRepository.insertFoo("name"); + transactionManager.commit(status); +} catch (RuntimeException e) { + transactionManager.rollback(status); +} +``` + +------------------------------------------------------------------------------------------------------------------------- +новая инфа +------------------------------------------------------------------------------------------------------------------------- +Для включения возможности управления транзакциями первым делом нужно разместить аннотацию @EnableTransactionManagement у класса-конфигурации @Configuration. + +Аннотация @EnableTransactionManagement означает, что классы, помеченные @Transactional, должны быть обернуты аспектом транзакций. Однако, если мы используем Spring Boot и имеем зависимости spring-data-* или spring-tx, то управление транзакциями будет включено по умолчанию. + +@EnableTransactionManagement отвечает за регистрацию необходимых компонентов Spring, таких как TransactionInterceptor и советы прокси (proxy advices- набор инструкций, выполняемых на точках среза - Pointcut). Регистрируемые компоненты помещают перехватчик в стек вызовов при вызове методов @Transactional. + +Spring создает прокси для всех классов, помеченных @Transactional (либо если любой из методов класса помечен этой аннотацией). Прокси-объекты позволяют Spring Framework вводить транзакционную логику до и после вызываемого метода -главным образом для запуска и коммита/отката транзакции. + +Если мы разместим аннотацию @Transactional над классом @Service, то все его методы станут транзакционными. Так, при вызове, например, метода save() произойдет примерно следующее: + +1. Вначале мы имеем: + - класс TransactionInterceptor, у которого основной метод invoke(...), внутри которого вызывается метод класса-родителя TransactionAspectSupport:invokeWithinTransaction(...), в рамках которого происходит магия транзакций. + - TransactionManager: решает, создавать ли новый EntityManager и/или транзакцию. + - EntityManager proxy: EntityManager - это интерфейс, и то, что внедряется в бин в слое DAO на самом деле не является реализацией EntityManager. В это поле внедряется EntityManager proxy, который будет перехватывать обращение к полю EntityManager и делегировать выполнение конкретному EntityManager в рантайме. Обычно EntityManager proxy представлен классом SharedEntityManagerInvocationHandler. +2. Transaction Interceptor + +В TransactionInterceptor отработает код до работы метода save(), в котором будет определено, выполнить ли метод save() в пределах уже существующей транзакции БД или должна стартовать новая отдельная транзакция. TransactionInterceptor сам не содержит логики по принятию решения, решение начать новую транзакцию, если это нужно, делегируется TransactionManager. Грубо говоря, на данном этапе наш метод будет обёрнут в try-catch и будет добавлена логика до его вызова и после: +```java +try { + transaction.begin(); + // логика до + service.save(); + // логика после + transaction.commit(); + } catch(Exception ex) { + transaction.rollback(); + throw ex; + } +``` + +3. TransactionManager + +Менеджер транзакций должен предоставить ответ на два вопроса: + - Должен ли создаться новый EntityManager? + - Должна ли стартовать новая транзакция БД? TransactionManager принимает решение, основываясь на следующих фактах: + - выполняется ли хоть одна транзакция в текущий момент или нет; + - атрибута «propagation» у метода, аннотированного @Transactional (для примера, значение REQUIRES_NEW всегда стартует новую транзакцию). + +Если TransactionManager решил создать новую транзакцию, тогда: + - Создается новый EntityManager; + - EntityManager «привязывается» к текущему потоку (Thread); + - «Получается» соединение из пула соединений БД; + - Соединение «привязывается» к текущему потоку. + +И EntityManager и это соединение привязываются к текущему потоку, используя переменные ThreadLocal. +4. EntityManager proxy Когда метод save() слоя Service делает вызов метода save() слоя DAO, внутри которого вызывается, например, entityManager.persist(), то не происходит вызов метода persist()напрямую у EntityManager, записанного в поле класса DAO. Вместо этого метод вызывает EntityManager proxy, который достает текущий EntityManager для нашего потока, и у него вызывается метод persist(). +5. Отрабатывает DAO-метод save(). +6. TransactionInterceptor Отработает код после работы метода save(), а именно будет принято решение по коммиту/откату транзакции. + +Кроме того, если мы в рамках одного метода сервиса обращаемся не только к методу save(), а к разным методам Service и DAO, то все они буду работать в рамках одной транзакции, которая оборачивает этот метод сервиса. + +Вся работа происходит через прокси-объекты разных классов. Представим, что у нас в классе сервиса только один метод с аннотацией @Transactional, а остальные нет. Если мы вызовем метод с @Transactional, из которого вызовем метод без @Transactional, то оба будут отработаны в рамках прокси и будут обернуты в нашу транзакционную логику. Однако, если мы вызовем метод без @Transactional, из которого вызовем метод с @Transactional, то они уже не будут работать в рамках прокси и не будут обернуты в нашу транзакционную логику. + +------------------------------------------------------------------------------------------------------------------------- +старая инфа +------------------------------------------------------------------------------------------------------------------------- Аннотация сама по себе определяет область действия одной транзакции БД. Транзакция БД происходит внутри области действий persistence context. Persistence контекстом в JPA является EntityManager, который использует внутри класс Session ORM-фреймворка Hibernate (при использовании Hibernate как persistence провайдера). Persistence контекст это объект-синхронайзер, который отслеживает состояния ограниченного набора Java объектов и синхронизирует изменения состояний этих объектов с состоянием соответствующих записей в БД. @@ -549,24 +1122,44 @@ __EntityManager proxy__ У @Transactional есть ряд параметров: + @Transactional (isolation=Isolation.READ_COMMITTED) - уровень изоляции. + @Transactional(timeout=60) - По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы. Сообщает менеджеру tx о продолжительности времени, чтобы дождаться простоя tx, прежде чем принять решение об откате не отвечающих транзакций. -+ @Transactional(propagation=Propagation.REQUIRED) - (Если не указано, распространяющееся поведение по умолчанию — REQUIRED.) -Указывает, что целевой метод не может работать без другой tx. Если tx уже запущен до вызова этого метода, то он будет продолжаться в том же tx, или новый tx начнется вскоре после вызова этого метода. ++ @Transactional(propagation=Propagation.REQUIRED_NEW) - (Если не указано, распространяющееся поведение по умолчанию — REQUIRED.) + +Когда вызывается метод с @Transactional происходит особая уличная магия: proxy, который создал Spring, создаёт persistence context (или соединение с базой), открывает в нём транзакцию и сохраняет всё это в контексте нити исполнения (натурально, в ThreadLocal). По мере надобности всё сохранённое достаётся и внедряется в бины. Привязка транзакций к нитям (threads) позволяет использовать семантику серверов приложений J2EE, в которой гарантируется, что каждый запрос получает свою собственную нить. + +Таким образом, если в вашем коде есть несколько параллельных нитей, у вас будет и несколько параллельных транзакций, которые будут взаимодействовать друг с другом согласно уровням изоляции. Но что произойдёт, если один метод с @Transactional вызовет другой метод с @Transactional? В Spring можно задать несколько вариантов поведения, которые называются правилами распространения. + +__REQUIRES__ - При входе в @Transactional метод будет использована уже существующая транзакция или создана новая транзакция, если никакой ещё нет + +__REQUIRES_NEW__ - Транзакция всегда создаётся при входе метод с Propagation.REQUIRES_NEW, ранее созданные транзакции приостанавливаются до момента возврата из метода. -__REQUIRES_NEW__ -Указывает, что новый tx должен запускаться каждый раз при вызове целевого метода. Если уже идет tx, он будет приостановлен, прежде чем запускать новый. +__NESTED__ — корректно работает только с базами данных, которые умеют savepoints (Postgres в том числе). Savepoints — транзакции внутри транзакций. Savepoint позволяет сохранить какое-либо состояние внутри транзакции и, при необходимости, откатиться к нему, не откатывая всю транзакцию. При входе в метод в уже существующей транзакции создаётся savepoint, который по результатам выполнения метода будет либо сохранён, либо откачен. Все изменения, внесённые методом, подтвердятся только поздее, с подтверждением всей транзакции. Если текущей транзакции не существует, будет создана новая. -__MANDATORY__ - Указывает, что для целевого метода требуется активный tx. Если tx не будет продолжаться, он не сработает, выбросив исключение. +__MANDATORY__ - обратный по отношению к REQUIRES_NEW: всегда используется существующая транзакция и кидается исключение, если текущей транзакции нет. -__SUPPORTS__ - Указывает, что целевой метод может выполняться независимо от tx. Если tx работает, он будет участвовать в том же tx. Если выполняется без tx, он все равно будет выполняться, если ошибок не будет. Методы, которые извлекают данные, являются лучшими кандидатами для этой опции. +__SUPPORTS__ - метод с этим правилом будет использовать текущую транзакцию, если она есть, либо будет исполнятся без транзакции, если её нет. Методы, которые извлекают данные, являются лучшими кандидатами для этой опции. -__NOT_SUPPORTED__ - Указывает, что целевой метод не требует распространения контекста транзакции. -В основном те методы, которые выполняются в транзакции, но выполняют операции с оперативной памятью, являются лучшими кандидатами для этой опции. +__NOT_SUPPORTED__ - При входе в метод текущая транзакция, если она есть, будет приостановлена и метод будет выполняться без транзакции. В основном те методы, которые выполняются в транзакции, но выполняют операции с оперативной памятью, являются лучшими кандидатами для этой опции. -__NEVER__ - Указывает, что целевой метод вызовет исключение, если выполняется в транзакционном процессе. -Этот вариант в большинстве случаев не используется в проектах. +__NEVER__ - явно запрещает исполнение в контексте транзакции. Если при входе в метод будет существовать транзакция, будет выброшено исключение. Этот вариант в большинстве случаев не используется в проектах. -+ @Transactional (rollbackFor=Exception.class) - Значение по умолчанию: rollbackFor=RunTimeException.class В Spring все классы API бросают RuntimeException, это означает, что если какой-либо метод не выполняется, контейнер всегда откатывает текущую транзакцию. Проблема заключается только в проверенных исключениях. Таким образом, этот параметр можно использовать для декларативного отката транзакции, если происходит Checked Exception. ++ @Transactional (rollbackFor=Exception.class) - Значение по умолчанию: rollbackFor=RunTimeException.class В Spring все классы API бросают RuntimeException, это означает, что если какой-либо метод не выполняется, контейнер всегда откатывает текущую транзакцию. Проблема заключается только в проверенных исключениях - при них транзакция пройдет в БД даже при ошибке. С этим параметром проверяемые исключения тоже будут откатываться при ошибке + @Transactional (noRollbackFor=IllegalStateException.class) - Указывает, что откат не должен происходить, если целевой метод вызывает это исключение. Если внутри метода с @Transactional есть другой метод с аннотацией @Transactional (вложенная транзакция), то отработает только первая (в которую вложенна). Из-за особенностей создания proxy. Но у аннотации @Transactional можно указать параметры. +Куда же ставить @Transactional? +Классическое приложение обычно имеет многослойную архитектуру: + +контроллеры > слой логики > слой доступа к данным > слой ORM + +Где здесь место для @Transactional? Слой ORM обычно никто не пишет сам и использует какое-либо стандартное решение, в которое аннотации не вставишь. + +Слой доступа к данным обычно представляет собой набор классов, методы которых реализуют тот или иной запрос. Получается, что если каждый метод аннотировать @Transactional, то, с одной стороны, работать это конечно будет, а с другой стороны теряется смысл транзакций, как логического объединения нескольких запросов в одну единицу работы. Ведь в таком случае у каждого метода, то есть у каждого запроса, будет своя, собственная, транзакция. + +Слой логики представляется идеальным местом для @Transactional: именно здесь набор запросов к базе оформляется в единую осмысленную операцию в приложении. Зная, что делает ваше приложение, вы можете четко разграничить логические единицы работы в нём и расставить границы транзакций. + +Слой контроллеров тоже может быть неплохим местом для @Transactional, но у него есть два недостатка, по сравнению со слоем логики. Во первых он взаимодействует с пользователем, напрямую или через сеть, что может делать транзакции длиннее: метод будет ждать отправки данных или реакции пользователя и в этом время продолжать удерживать транзакцию и связанные с ней блокировки. Во вторых это нарушает принцип разделения ответственности: код, который должен быть ответственен за интерфейс с внешним миром, становится ответственен и за часть управления логикой приложения. + +И последнее — никогда не аннотируйте интерфейсы. Аннотации не наследуются и поэтому, в зависимости от настроек Spring, вы можете внезапно оказаться фактически без своих @Transactional + [к оглавлению](#spring) ## Как Spring работает с DAO @@ -639,6 +1232,10 @@ public interface RoleRepository extends JpaRepository{ @Transactional @Query(value = "INSERT INTO user_roles (user_id, role_id) VALUE (:user_id, :role_id)", nativeQuery = true) void insertRoles(@Param("user_id) Long user_id, @Param("role_id") Long role_id); + + //можно использовать с EntityGraph, обычным, не NamedEntityGraph + @EntityGraph(value = "customer.products") + List findAll(@Nullable Specification specification) ``` ## Конфигурация Spring Data @@ -693,17 +1290,82 @@ public class JpaConfig { ## Spring Security Spring Security предоставляет широкие возможности для защиты приложения. Кроме стандартных настроек для аутентификации, авторизации и распределения ролей и маппинга доступных страниц, ссылок и т.п., предоставляет защиту от различных вариантов атак -+ SecurityContextHolder, в нем содержится информация о текущем контексте безопасности приложения, который включает в себя подробную информацию о пользователе(Principal) работающем в настоящее время с приложением. По умолчанию SecurityContextHolder используетThreadLocal для хранения такой информации, что означает, что контекст безопасности всегда доступен для методов исполняющихся в том же самом потоке. Для того что бы изменить стратегию хранения этой информации можно воспользоваться статическим методом класса SecurityContextHolder.setStrategyName(String strategy). Более подробно SecurityContextHolder -+ SecurityContext, содержит объект Authentication и в случае необходимости информацию системы безопасности, связанную с запросом от пользователя. -+ Authentication представляет пользователя (Principal) с точки зрения Spring Security. -+ GrantedAuthority отражает разрешения выданные пользователю в масштабе всего приложения, такие разрешения (как правило называются «роли»), например ROLE_ANONYMOUS, ROLE_USER, ROLE_ADMIN. -+ UserDetails предоставляет необходимую информацию для построения объекта Authentication из DAO объектов приложения или других источников данных системы безопасности. Объект UserDetailsсодержит имя пользователя, пароль, флаги: isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled и Collection — прав (ролей) пользователя. -+ UserDetailsService, используется чтобы создать UserDetails объект путем реализации единственного метода этого интерфейса +Spring Security - это список фильтров в виде класса FilterChainProxy, интегрированного в контейнер сервлетов, и в котором есть поле List. Каждый фильтр реализует какой-то механизм безопасности. Важна последовательность фильтров в цепочке. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring8.png) + +Когда мы добавляем аннотацию @EnableWebSecurity добавляется DelegatingFilterProxy, его задача заключается в том, чтобы вызвать цепочку фильтров (FilterChainProxy) из Spring Security. + +В Java-based конфигурации цепочка фильтров создается неявно. + +Если мы хотим настроить свою цепочку фильтров, мы можем сделать это, создав класс, конфигурирующий наше Spring Security приложение, и имплементировав интерфейс WebSecurityConfigurerAdapter. В данном классе, мы можем переопределить метод: +```java +@Override +protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .authorizeRequests(); +} +``` + +Именно этот метод конфигурирует цепочку фильтров Spring Security и логика, указанная в этом методе, настроит цепочку фильтров. + +__Основные классы и интерфейсы__ + +__SecurityContext__ - интерфейс, отражающий контекст безопасности для текущего потока. Является контейнером для объекта типа Authentication. (Аналог - ApplicationContext, в котором лежат бины). + +По умолчанию на каждый поток создается один SecurityContext. SecurityContext-ы хранятся в SecurityContextHolder. + +Имеет только два метода: getAuthentication() и setAuthentication(Authentication authentication). +__SecurityContextHolder__ - это место, где Spring Security хранит информацию о том, кто аутентифицирован. Класс, хранящий в ThreadLocal SecurityContext-ы для каждого потока, и содержащий статические методы для работы с SecurityContext-ами, а через них с текущим объектом Authentication, привязанным к нашему веб-запросу. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring9.png) + +__Authentication__ - объект, отражающий информацию о текущем пользователе и его привилегиях. Вся работа Spring Security будет заключаться в том, что различные фильтры и обработчики будут брать и класть объект Authentication для каждого посетителя. Кстати объект Authentication можно достать в Spring MVC контроллере командой SecurityContextHolder.getContext().getAuthentication(). Authentication имеет реализацию по умолчанию - класс UsernamePasswordAuthenticationToken, предназначенный для хранения логина, пароля и коллекции Authorities. +__Principal__ - интерфейс из пакета java.security, отражающий учетную запись пользователя. В терминах логин-пароль это логин. В интерфейсе Authentication есть метод getPrincipal(), возвращающий Object. При аутентификации с использованием имени пользователя/пароля Principal реализуется объектом типа UserDetails. +__Credentials__ - любой Object; то, что подтверждает учетную запись пользователя, как правило пароль (отпечатки пальцев, пин - всё это Credentials, а владелец отпечатков и пина - Principal). +__GrantedAuthority__ - полномочия, предоставленные пользователю, например, роли или уровни доступа. +__UserDetails__ - интерфейс, представляющий учетную запись пользователя. Как правило модель нашего пользователя должна имплементировать его. Она просто хранит пользовательскую информацию в виде логина, пароля и флагов isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled, а также коллекции прав (ролей)пользователя. Данная информация позже инкапсулируется в объекты Authentication. +__UserDetailsService__ - интерфейс объекта, реализующего загрузку пользовательских данных из хранилища. Созданный нами объект с этим интерфейсом должен обращаться к БД и получать оттуда юзеров. используется чтобы создать UserDetails объект путем реализации единственного метода этого интерфейса ```java UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; ``` -Позволяет получить из источника данных объект пользователя и сформировать из него объект UserDetails который будет использоваться контекстом Spring Security. +__AuthenticationManager__ - основной стратегический интерфейс для аутентификации. Имеет только один метод, который срабатывает, когда пользователь пытается аутентифицироваться в системе: +```java +public interface AuthenticationManager { +Authentication authenticate(Authentication authentication) +throws AuthenticationException; +} +``` +AuthenticationManager может сделать одну из 3 вещей в своем методе authenticate(): + +1. вернуть Authentication (с authenticated=true), если предполагается, что вход осуществляет корректный пользователь. +2. бросить AuthenticationException, если предполагается, что вход осуществляет некорректный пользователь. +3. вернуть null, если принять решение не представляется возможным. + +Наиболее часто используемая реализация AuthenticationManager - родной класс ProviderManager, который содержит поле private Listproviders со списком AuthenticationProvider-ов и итерирует запрос аутентификации по этому списку AuthenticationProvider-ов. Идея такого разделения - поддержка различных механизмов аутентификации на сайтах. + +AuthenticationProvider - интерфейс объекта, выполняющего аутентификацию. Имеет массу готовых реализаций. Также можем задать свой тип аутентификации. Как правило в небольших проектах одна логика аутентификации - по логину и паролю. В проектах побольше логик может быть несколько: Google-аутентификация и т.д., и для каждой из них создается свой объект AuthenticationProvider. + +AuthenticationProvider немного похож на AuthenticationManager, но у него есть дополнительный метод, позволяющий вызывающей стороне спрашивать, поддерживает ли он переданный ему объект Authentication, возможно этот AuthenticationProvider может поддерживать только аутентификацию по логину и паролю, но не поддерживать Googleаутентификацию: +```java +boolean supports(java.lang.Class authentication) +``` +PasswordEncoder - интерфейс для шифрования/расшифровывания паролей. Одна из популярных реализаций - BCryptPasswordEncoder. + +В случае, если нам необходимо добавить логику при успешной/неудачной аутентификации, мы можем создать класс и имплементировать интерфейсы AuthenticationSuccessHandler и AuthenticationFailureHandler соответственно, переопределив их методы. + +Как это работает с формой логина и UserDetailsService: + - Пользователь вводит в форму и отправляет логин и пароль. + - UsernamePasswordAuthenticationFilter создает объект Authentication - UsernamePasswordAuthenticationToken, где в качестве Principal - логин, а в качестве Credentials - пароль. + - Затем UsernamePasswordAuthenticationToken передаёт объект Authentication с логином и паролем AuthenticationManager-у. + - AuthenticationManager в виде конкретного класса ProviderManager внутри своего списка объектов AuthenticationProvider, имеющих разные логики аутентификации, пытается аутентифицировать посетителя, вызывая его метод authenticate(). У каждого AuthenticationProvider-а: + 1 Метод authenticate() принимает в качестве аргумента незаполненный объект Authentication, например только с логином и паролем, полученными в форме логина на сайте. Затем с помощью UserDetailsService метод идёт в БД и ищет такого пользователя. + 2 Если такой пользователь есть в БД, AuthenticationProvider получает его из базы в виде объекта UserDetails. Объект Authentication заполняется данными из UserDetails - в него включаются Authorities, а в Principal записывается сам объект UserDetails, содержащий пользователя. + 3 Затем этот метод возвращает заполненный объект Authentication (прошли аутентификацию). Вызывается AuthenticationSuccessHandler. + 4 Если логин либо пароль неверные, то выбрасывается исключение. Вызывается AuthenticationFailureHandler. + - Затем этот объект Authentication передается в AccessDecisionManager и получаем решение на получение доступа к запрашиваемой странице (проходим авторизацию). + Аннотации: + @EnableGlobalMethodSecurity - включает глобальный метод безопасности. + @EnableWebMvcSecurity - "включает" Spring Security. Не будет работать, если наш класс не наследует WebSecurityConfigurerAdapter @@ -720,6 +1382,8 @@ UserDetails loadUserByUsername(String username) throws UsernameNotFoundException Starter-пакеты представляют собой набор удобных дескрипторов зависимостей, которые можно включить в свое приложение. Это позволит получить универсальное решение для всех, связанных со Spring технологий, избавляя программиста от лишнего поиска примеров кода и загрузки из них требуемых дескрипторов зависимостей. Например, если вы хотите начать использовать Spring Data JPA для доступа к базе данных, просто включите в свой проект зависимость spring-boot-starter-data-jpa и все будет готово (вам не придется искать совместимые драйверы баз данных и библиотеки Hibernate) +Например, если вы добавите Spring-boot-starter-web, Spring Boot автоматически сконфигурирует такие зарегистрированные бины, как DispatcherServlet, ResourceHandlers, MessageSource. Если вы используете spring-boot-starter-jdbc, Spring Boot автоматически регистрирует бины DataSource, EntityManagerFactory, TransactionManager и считывает информацию для подключения к базе данных из файла application.properties + В основе "магии" Spring Boot нет ничего магического, он использует совершенно базовые понятия из Spring Framework. В кратком виде процесс можно описать так: + Аннотация @SpringBootApplication включает сканирование компонентов и авто-конфигурацию через аннотацию @EnableAutoConfiguration + @EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector @@ -742,6 +1406,23 @@ Starter-пакеты представляют собой набор удобны Можно отказаться от использования механизма автоконфигурации, вместо этого указывая необходимые автоконфигурации вручную. Для этого надо избавиться от аннотаций @SpringBootApplication и @EnableAutoConfiguration в коде вашего проекта, а для указания нужных конфигурационных классов использовать аннотации @SpringBootConfiguration и @ImportAutoConfiguration. Однако стоит помнить, что используемые автоконфигурации всё ещё могут содержать неиспользуемые компоненты. +__Как происходит автоконфигурация в Spring Boot:__ + +1. Отмечаем main класс аннотацией @SpringBootApplication (аннотация инкапсулирует в себе:@SpringBootConfiguration, @ComponentScan, @EnableAutoConfiguration), таким образом наличие @SpringBootApplication включает сканирование компонентов, автоконфигурацию и показывает разным компонентам Spring (например, интеграционным тестам), что это Spring Boot приложение. +```java +@SpringBootApplication + public class DemoApplication { + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + } +``` +2. @EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector. Этот класс не объявляет бины сам, а использует так называемые фабрики. +3. Класс EnableAutoConfigurationImportSelector смотрит в файл META-INF/spring.factories и загружает оттуда список значений, которые являются именами классов (авто)конфигураций, которые Spring Boot импортирует. Т.е. аннотация @EnableAutoConfiguration просто импортирует ВСЕ (более 150) перечисленные в spring.factories конфигурации, чтобы предоставить нужные бины в контекст приложения. +4. Каждая из этих конфигураций пытается сконфигурировать различные аспекты приложения(web, JPA, AMQP и т.д.), регистрируя нужные бины. Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath (@ConditionalOnClass), наличии существующего бина (@ConditionalOnBean), отсуствии бина (@ConditionalOnMissingBean) и т.п. Таким образом наличие конфигурации не значит, что бин будет создан, и в большинстве случаев конфигурация ничего делать и создавать не будет. +5. Созданный в итоге AnnotationConfigEmbeddedWebApplicationContext ищет в том же DI контейнере фабрику для запуска embedded servlet container. +6. Servlet container запускается, приложение готово к работе! + [к оглавлению](#spring) ## Starter packs @@ -763,6 +1444,8 @@ public class VKontakteAutoConfiguration { + Создание файла, позволяющего SpringBoot найти наш AutoConfiguration класс. Для этого существует специальный файл spring.factories, который нужно поместить в META-INF папку получающегося jar-файла. В этом файле нам надо указать наш AutoConfiguration-класс. + Подключить получившийся jar-файл к Spring Boot проекту и задать в конфигурации пути на класс, который создали до этого. +https://www.youtube.com/watch?v=nGfeSo52_8A&t=1735s (с 30мин) + [к оглавлению](#spring) ## Как внедрить java.util.Properties в Spring Bean diff --git a/sql.md b/sql.md index 6d30a6c..26249ef 100644 --- a/sql.md +++ b/sql.md @@ -30,6 +30,7 @@ https://tproger.ru/articles/sql-interview-questions/ + [Что делает оператор `MERGE`?](#Что-делает-оператор-merge) + [В чем отличие между операторами `DELETE` и `TRUNCATE`?](#В-чем-отличие-между-операторами-delete-и-truncate) + [Что делает оператор `EXPLAIN`?](#Что-делает-оператор-merge) ++ [Что такое _«RETURNING»_?](#Что-такое-RETURNING») + [Что такое _«хранимая процедура»_?](#Что-такое-хранимая-процедура) + [Что такое _«триггер»_?](#Что-такое-триггер) + [Что такое _«курсор»_?](#Что-такое-курсор) @@ -198,7 +199,7 @@ __ORDER BY__ упорядочивает вывод запроса согласн [к оглавлению](#sql) ## Для чего используется оператор `GROUP BY`? -`GROUP BY` используется для агрегации записей результата по заданным признакам-атрибутам. +`GROUP BY` используется для агрегации записей результата по заданному столбцу. [к оглавлению](#sql) @@ -272,6 +273,23 @@ SELECT * FROM UNIVERSITY WHERE NAME LIKE '%o'; ## Для чего применяется ключевое слово `UNION`? В языке SQL ключевое слово `UNION` применяется для объединения результатов двух SQL-запросов в единую таблицу, состоящую из схожих записей. Оба запроса должны возвращать одинаковое число столбцов и совместимые типы данных в соответствующих столбцах. Необходимо отметить, что `UNION` сам по себе не гарантирует порядок записей. Записи из второго запроса могут оказаться в начале, в конце или вообще перемешаться с записями из первого запроса. В случаях, когда требуется определенный порядок, необходимо использовать `ORDER BY`. +С удалением дублей: +```sql +SELECT * FROM имя_таблицы1 WHERE условие + UNION SELECT * FROM имя_таблицы2 WHERE условие +``` +Без удаления дублей: +```sql +SELECT * FROM имя_таблицы1 WHERE условие + UNION ALL SELECT * FROM имя_таблицы2 WHERE условие +``` +Можно объединять не две таблицы, а три или более: +```sql +SELECT * FROM имя_таблицы1 WHERE условие + UNION SELECT * FROM имя_таблицы2 WHERE условие + UNION SELECT * FROM имя_таблицы3 WHERE условие + UNION SELECT * FROM имя_таблицы4 WHERE услови +``` [к оглавлению](#sql) ## Какие ограничения на целостность данных существуют в SQL? @@ -388,6 +406,27 @@ select_type – тип запроса SELECT. [к оглавлению](#sql) +## Что такое _«RETURNING»_? +Иногда бывает полезно получать данные из модифицируемых строк в процессе их обработки. Это возможно с использованием предложения RETURNING, которое можно задать для команд INSERT, UPDATE и DELETE. Применение RETURNING позволяет обойтись без дополнительного запроса к базе для сбора данных и это особенно ценно, когда как-то иначе трудно получить изменённые строки надёжным образом. + +Точно работает в PostgreSQL + +```java +INSERT INTO users (firstname, lastname) VALUES ('Joe', 'Cool') RETURNING id; + +В команде UPDATE данные, выдаваемые в RETURNING, образуются новым содержимым изменённой строки. Например: +UPDATE products SET price = price * 1.10 + WHERE price <= 99.99 + RETURNING name, price AS new_price; + +В команде DELETE данные, выдаваемые в RETURNING, образуются содержимым удалённой строки. Например: +DELETE FROM products + WHERE obsoletion_date = 'today' + RETURNING *; + ``` + +[к оглавлению](#sql) + ## Что такое _«хранимая процедура»_? __Хранимая процедура__ — объект базы данных, представляющий собой набор SQL-инструкций, который хранится на сервере. Хранимые процедуры очень похожи на обыкновенные процедуры языков высокого уровня, у них могут быть входные и выходные параметры и локальные переменные, в них могут производиться числовые вычисления и операции над символьными данными, результаты которых могут присваиваться переменным и параметрам. В хранимых процедурах могут выполняться стандартные операции с базами данных (как DDL, так и DML). Кроме того, в хранимых процедурах возможны циклы и ветвления, то есть в них могут использоваться инструкции управления процессом исполнения. diff --git a/test.md b/test.md index 92afad0..d680c79 100644 --- a/test.md +++ b/test.md @@ -2,6 +2,7 @@ # Тестирование + [Что такое _«модульное тестирование»_?](#Что-такое-модульное-тестирование) ++ [Что такое _«компонентное тестирование»_?](#Что-такое-компонентное-тестирование) + [Что такое _«интеграционное тестирование»_?](#Что-такое-интеграционное-тестирование) + [Чем интеграционное тестирование отличается от модульного?](#Чем-интеграционное-тестирование-отличается-от-модульного) + [Какие существуют виды тестовых объектов?](#Какие-существуют-виды-тестовых-объектов) @@ -10,6 +11,8 @@ + [Какие аннотации фикстур существуют в JUnit?](#Какие-аннотации-фикстур-существуют-в-junit) + [Для чего в JUnit используется аннотация `@Ignore`?](#Для-чего-в-junit-используется-аннотация-ignore) +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Test1.png) + ## Что такое _«модульное тестирование»_? __Модульное/компонентное тестирование (unit testing)__ - процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы. Идея состоит в том, чтобы писать тесты для каждой нетривиальной функции или метода. Это позволяет достаточно быстро проверить, не привело ли очередное изменение кода к регрессии, то есть к появлению ошибок в уже оттестированных местах программы, а также облегчает обнаружение и устранение таких ошибок. @@ -21,6 +24,13 @@ __Модульное/компонентное тестирование (unit tes [к оглавлению](#Тестирование) +## Что такое _«компонентное тестирование»_? +Проверяет функциональность и ищет дефекты в частях приложения, которые доступны и могут быть протестированы по-отдельности (модули программ, объекты, классы, функции и т.д.). Обычно проводится вызывая код, который необходимо проверить и при поддержке сред разработки, таких как фреймворки (frameworks - каркасы) для модульного тестирования или инструменты для отладки. + +По-существу компонентные и модульные тестирования представляют одно и тоже, разница лишь в том, что в компонентном тестировании в качестве параметров функций используют реальные объекты и драйверы, а в модульном тестировании - конкретные значения. + +[к оглавлению](#Тестирование) + ## Что такое _«интеграционное тестирование»_? __Интеграционное тестирование (integration testing)__ — это тестирование, проверяющие работоспособность двух или более модулей системы в совокупности — то есть нескольких объектов как единого блока. В тестах взаимодействия же тестируется конкретный, определенный объект и то, как именно он взаимодействует с внешними зависимостями. diff --git a/toResolve.md b/toResolve.md new file mode 100644 index 0000000..dafdaa4 --- /dev/null +++ b/toResolve.md @@ -0,0 +1,118 @@ +[Вопросы для собеседования](README.md) + +# Вопросы для разбора + +1. Java Core: С какой проблемой можно столкнуться при увеличении размера heap памяти. Почему программисты стараются излишне не расширять ее +2. Java Core: Ускоряет ли вычисление программы использование parallelStream() в Stream ? В каких случаях да, а в каких нет +3. Java Core: какой размер у String Pool? +4. Java Core: какой GC используется по дефолту в Java 8/11 +5. Java Core: что такое Stop The World +6. Java Core: чем лямбда отличается от анонимного класса +7. Java Core: какие методы можно вызвать у Throwable +8. Java Core: назови классы, которые наследуются не от Object +9. Maven: как передавать стартовые параметры через мавен +10. Java Core: назовите Immutable коллекции +11. Java Core: как сделать иммутабельным класс, у которого в полях находятся ссылочные неиммутабельные типы (final не поможет, потому что финализируется ссылка, а не объект, и сам объект можно будет изменить) +12. Java Core: Максимальное кол-во элементов в массиве? Максимальный размер ArrayList? Максимальный размер LinkedList? Почему в LinkedList лучше не использовать size() при итеррировании, и как лучше итеррироваться? +13. SQL: что такое Explain и чем Explain отличается от Explain Analyse +14. Spring: @Value отрабатывает до вызова конструктора или после? +15. Spring: На каком этапе происходит внедрение зависимостей при использовании @Autowired над конструктором? +16. Spring: как выбрать профиль +17. Spring: Какой из трех способов автовайринга рекомендуется использовать разработчиками спринга и по каким причинам. +18. Spring: можно ли создать два бина со скоупом сингтон одного класса +19. Spring: мы создали контроллер и инжектим бин со скоупом Session. Как спринг будет подставлять каждой сессии новый бин, если контекс инициализируется со стартом приложения +20. Spring: какие исключения может обрабатывать @Transactional по дефолту. Может ли он обработать пробрасываемое исключение + +# Микросервисная архитектура. Основные принципы. Отличия от Монолита и SOA +1. 12-ти факторная модель создания облачных приложений +2. IaaS, PaaS, SaaS https://gigacloud.ua/ru/blog/navchannja/hmarna-piramida-iaas-paas-i-saas +3. CAP теорема +4. Паттерны микросервисной архитектуры. https://mcs.mail.ru/blog/26-osnovnyh-patternov-mikroservisnoj-razrabotki +5. Паттерны интеграции микросервисов https://www.enterpriseintegrationpatterns.com/patterns/messaging/ + +# Apache Kafka +https://www.youtube.com/watch?v=-AZOi3kP9Js Основы кафки + +https://www.youtube.com/watch?v=c_mkpVg5rlg Обзор брокеров сообщений + +https://www.youtube.com/watch?v=Y1eSeEJDses вебинар по обзору некоторых фишек в кафке + +1. Что такое очередь сообщений. +2. Основные концепции очередей +3. ? Kafka vs Rabbit MQ +4. Основные сущности Kafka + +# Kafka Cluster. Zookeaper +5. Zookeper. Хранение метаданных кластера +6. Kafka кластер. Устройство +7. Партиционирование. Leader партиция. +8. Репликация +9. Настройка Kafka кластера для корректной работы партиционирования и репликации +10. Устройство файлового хранилища Kafka +11. TTL + +# Producer +13. Producer. Из каких шагов состоит инцициализация +14. Стратегии коммитинга. Гарантия доставки +15. Сериализация, Десериализация +16. Стратегии выбора партиции продюссером +17. Можно ли из топика (распределен по 3 партициям) прочитать сообщения в том же порядке, в котором они были записаны? Почему? +18. Как сделать так, чтобы все сообщения по одному клиенту попали в одну партицию? +19. Timestamp +20. Headers +21. Batch size. Linger time +22. Retry +# Consumer + +# Docker, Kubernetes, OpenShift. +1. Контейнеризация + +# Реактивщина +https://projectreactor.io/docs/core/release/api/ - список классов и методов по Project Reator + +https://habr.com/ru/post/565000/ - основной источник материалов + +1. Реактивное программирование. Основные принципы. Преимущество реактивного программирование над блокирующим. +2. Перечислите основные виды потерь на блокирующем типе программирования в web +3. Объясните понятие backpressure. Что оно дает. https://habr.com/ru/post/512724/ +4. При каком количестве запросов и нагрузке имеет реактивно построенное приложение начинает выигровать у приложения с блокироющим типом запросов. Приведите оценки +5. Основные библиотеки + +# Rector Core +1. Cтандарт спецификации Reactive Streams. +2. Reactive Streams: Основные интефейсы +3. Reactive Streams: Publisher +4. Reactive Streams: Subscriber +5. Основные модули Project Reactor +6. Reactor Core: основные интерфейсы +7. Flux Api. Основные методы https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html +8. Mono Api. Основные методы +9. Обработка ошибок +10. Тестирование +11. Параллельное выполнение. Scheduler +12. Backpressure: оператор request +13. Горячий и холодный паблишер. Что это. Как создать +14. Контекст: локальные переменные для контекста +15. Sinks. События +16. Отладка/Debug реактивной программы + +# Spring WebFlux (Расширить) +22. Реактивные Application Servers: Netty, Jetty, Tomcat, Servlet 3.1, HTTP 2.0 +23. Spring WebFlux. Для чего используется +24. Обработка запроса. Аннотационная модель: контроллеры на базе WebFlux +25. Обработка запроса. Функциональная модель: HandlerFunctions, RouterFunctions, +26. Spring Security WebFlux. +27. Отправка запросов. WebClient. Основные методы +28. WebSocket, RSocket +29. Тестирование WebFlux + +# R2DBC (Расширить) +30. Что такое R2DBC, Программы, реализующие драверы для R2DBC +31. SPRING DATA R2DBC +32. ReactiveCrudRepository +33. DatabaseClient. Оптравка SQL запросов напрямую в БД +34. Транзакции + +[к оглавлению](#Вопросы-для-разбора) + +[Вопросы для собеседования](README.md)