Библиотеки и стандарты
52. Что такое Hibernate? В чём разница между JPA и Hibernate?
Я думаю, чтобы ответить на данный вопрос, нам сперва нужно понять, что такое JPA. JPA — это спецификация, описывающая объектно-реляционное отображение простых Java объектов и предоставляющая API для сохранения, получения и управления такими объектами. То есть, как мы помним, реляционные базы данных (БД) представлены в виде множества связанных между собой таблиц. И JPA — общепринятый стандарт, который описывает, как объекты могут взаимодействовать с реляционными базами данных. Как видите, JPA — это что-то абстрактное и неосязаемое. Это как бы сама идея, подход.В то же время Hibernate — это конкретная библиотека, реализующая парадигмы JPA. То, есть с помощью этой библиотеки вы можете работать с реляционной базой данных через объекты, которые представляют данные с БД (Entity). Как говорят, данная библиотека очень близка к идеалам JPA и возможно, поэтому она стала популярна. А как вы понимаете, популярность использования — хороший аргумент для дальнешйей разработки и улучшений. К тому же за частым использованием стоит огромное комьюнити, которое разобрало уже все возможные и невозможные вопросы, связанные с данным инструментом. Вот пример книги, которая подробно разбирает все темные закоулки данной технологии. То есть, Hibernate максимально изучен и, получается, надежен. Собственно, не зря даже идеальная реализация JPA со стороны Spring-а под капотом как правило использует Hibernate.53. Что такое каскадность? Как она используется в Hibernate?
Как я и сказал ранее, в Hibernate взаимодействие ведется через объекты данных, называемые entity. Эти entity представляют какие-то конкретные таблицы в базе данных, и как вы помните, в Java классы могут содержать ссылки на другие классы. Эти отношения отражаются и на базе данных. В БД, как правило, это либо внешние ключи (для OneToOne, OneToMany, ManyToOne), либо промежуточные таблицы (для ManyToMany) Подробнее о взаимосвязи между сущностями можно почитать в этой статье. Когда в вашем entity есть ссылки на другие связанные сущности, над этими ссылками ставятся аннотации для указания типа связи: @OneToOne, @OneToMany, @ManyToOne, @ManyToMane, в чьих параметрах вы можете указать значение свойства — cascade — тип каскаданости для данной связи. У JPA есть специфические методы для взаимодействия с сущностями (persist, save, merge…). Каскадные типы как раз используются для того, чтобы показать, как должны себя вести связанные данные при использовании этих методов на целевую сущность. Итак, какие же существуют стратегии каскаскадности (типы каскадности)? Стандарт JPA подразумевает использование шести видов каскадности:PERSIST — операции сохранения будут происходить каскадно (для методов save() и persist()). То есть, если мы сохраняем сущность, связанную с другими сущностями, они также сохраняются в БД (если их ещё там нет)
MERGE — операции обновления будут происходить каскадно (для метода merge())
REMOVE — операции удаления происходят каскадно (метод remove())
ALL — содержит сразу три каскадные операции — PERSIST - MERGE - REMOVE
DETACH — связанные сущности не будут управляться сессией (метод detach()). То есть, при их изменении не будет автоматического изменения их данных в БД — они переводятся из состояния persistence в detached (сущность, не управляемая JPA)
REFRESH — при каждом обновлении сущности данными из БД (refresh() — обновляет detached объекты) связанные сущности обновляются так же. Например, вы изменили как-то данные, взятые из БД, и хотите вернуть их изначальные значения. В таком случае вам и пригодится данная операция.
REPLICATE — используется, когда у нас есть более одного источника данных и мы хотим, чтобы данные синхронизировались (метод Hibernate — replicate). У всех сущностей должны быть идентификаторы (id), чтобы не было проблем с их генерацией (чтобы для разных БД одна и та же сущность не имела разных id)
SAVE_UPDATE — каскадное сохранение/удаление (для метода Hibernate — saveOrUpdate)
LOCK — операция, обратная к DETACHED: переводит detached сущность обратно в состояние persistence, т.е. entity станет снова отслеживаемой текущей сессией
54. Может ли Entity класс быть абстрактным?
В спецификации JPA в пункте 2.1 The Entity Class есть строка: “И абстрактные, и конкретные классы могут быть сущностями”. То есть, ответ — да, абстрактный класс может быть сущностью и может быть аннотирован с помощью @Entity.55. Что такое entity manager? За что отвечает?
В первую очередь хотелось бы отметить, что EntityManager — один из ключевых компонентов JPA, который используется для взаимодействия сущностей с базой данных. В общем-то методы взаимодействия сущности с БД у него-то и вызываются (persist, merge, remove, detach)... Но также отмечу, что данный компонент как правило не является одним на всё приложение: чаще всего он легковесен, часто удаляется и создается новый с помощью EntityManagerFactory. Если проводить параллель с JDBC, где EntityManagerFactory будет являться аналогом DataSource, то EntityManager в свою очередь будет являться аналогом Connection. Ранее я упоминал про персистентную (persistence) сущность, как сущность, которая управляется текущим соединением. Так вот: эта сущность управляется именно EntityManager-ом, который тесно связан с текущим соединением и TransactionManager-ом, который отвечает за открытие/закрытие транзакций. Далее на рисунке ниже вы можете видеть жизненный цикл сущности:EntityManager управляет сущностью, когда она на этапе Managed (в это время она персистентная, т.к. имеет связь с EntityManager-ом). То есть, она уже не new и ещё не removed. Можно сказать, что когда сущность new или removed, она также и detached, т.к. она не управляется EntityManager-ом. Существуют разные стратегии для EntityManager-а. То есть, может быть один синглтоновый EntityManager на всё приложение, а может создаваться каждый раз новый, под каждое соединение. Если же вы используете Spring, то управление созданием/удалением EntityManager-а происходит автоматически под капотом (но это не значит, что нельзя настроить это под себя ^^). Стоит сказать, что один или несколько EntityManager-ов и образуют и persistence context. Persistence context — это среда в которой экземпляры сущностей синхронизируются с аналогичными сущностями в базе данных (как я и говорил, это работает только для персистентных сущностей). Если вы углубитесь в изучение JPA (что я очень вам рекомендую), то с данными понятием вы будете сталкиваться очень и очень часто.56. Что такое класс Assert? Зачем его использовать?
В JPA я о таком классе не слышал, поэтому предположу, что тут имеется ввиду класс JUnit библиотеки, которая используется для модульного тестирования кода. Класс данной библиотеки, Assert, используется для проверки результатов выполнения кода (assert — утверждение, что в определенном месте у вас определенное состояние/данные). Например, вы тестируете метод, который должен создавать кота. Вы запускаете метод и получаете некоторый результат:
Cat resultOfTest = createCat();
Но ведь вам нужно убедиться, что он был правильно создан, не так ли?
Поэтому вы до этого создали некого кота — expectedCat — вручную с точно такими параметрами, которые вы ожидаете от кота, полученного с метода createCat().
Далее вы и используете класс Assert для сверки полученных результатов:
Assert.assertEquals(resultOfTest, expectedCat);
Если коты будут отличаться, будет выброшено исключение AssertionError, которое говорит нам о том, что ожидаемые результаты не сходятся.
У класса Assert есть множество различных методов, которые покрывают множество задач по проверке ожидаемых результатов. Вот некоторые из них:assertTrue(<boolean>) — ожидаемое значение, полученное в качестве аргумента, должно быть true
assertFalse(<boolean>) — ожидаемое значение, полученное в качестве аргумента, должно быть false
assertNotEquals(<object1>, <object2>) — объекты, полученные в качестве аргументов, при сравнении посредством equals должны быть разными (false)
assertThrows(<ClassNameOfException>.class, <exceptionObject>) — ожидается, что вторым аргументом будет исключение класса, прописанного первым аргументом (т.е. как правило на месте второго аргумента вызывается метод, который должен бросать исключение нужного типа)
String
57. Дайте характеристику String в Java
String — стандартный класс в Java, отвечающий за хранение и манипуляции со строковыми значениями (последовательности символов), является immutable классом (об immutable я писал ранее), т.е. данные объектов данного класса невозможно изменить после создания. Хотелось бы сразу отметить, что классы StringBuilder и StringBuffer — это два фактически одинаковых класса с той лишь разницей, что один из них предназначен для использования в многопоточной среде (StringBuffer). Эти классы являются аналогами String, но в отличие от него они изменяемы. То есть объекты после создания допускают модификацию строки, которую представляют, без создания нового объекта. Собственно, методы отличаются от стандартных методов String и направлены на удовлетворение потребностей по изменению строки (не зря же builder-ом назвали). Подробнее о String, StringBuffer и StringBuilder читайте вот в этой статье.58. Какие есть способы создания объекта String? Где он создаётся?
Самый привычный способ создания строки — простое задание нужного нам значения в двойных скобках:
String str = "Hello World!";
Также можно сделать это непосредственно через new:
String str = new String("Hello World!");
Можно создать строку и отталкиваясь от массива символов:
char[] charArr = {'H','e','l','l','o',' ', 'W','o','r','l','d','!'};
String str = new String(charArr);
Как результат работы метода toString на некотором объекте:
String str = someObject.toString();
Как результат работы любого другого метода возвращает строковое представление. Например:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str = reader.readLine();
Как вы поняли, способов создания строки может быть очень и очень много.
При создании объекта String он сохраняется в строковом пуле, о котором подробнее мы поговорим в одном из вопросов далее.59. Как сравнить две строки в Java и как их отсортировать?
Для сравнения значений в Java используется знак двойного равно ==. Если бы нам нужно было сравнивать некоторые простые значения вроде int, мы бы воспользовались им. Но для сравнения полноценных объектов данный способ неприменим. В таком случае это будет лишь сравнение ссылок — указывают ли они на один и тот же они объект или нет. То есть при сравнении двух объектов с совершенно одинаковыми значениями внутренних полей сравнение через == даст результат false: несмотря на одинаковость полей объектов, сами объекты занимают разные ячейки памяти. А объекты класса String, несмотря на обманчивую простоту, всё же являются объектами. И сравнение через == для них также неприменимо (даже несмотря на наличие строкового пула). Тут за дело берется стандартный метод класса Object — equals, который необходимо переопределить в классе для его корректной работы (иначе по умолчанию он сравнивает через ==). В классе String он переопределен, значит просто берем и используем его:
String firstStr = "Hello World!";
String secondStr = "Hello World!";
boolean isEquals = firstStr.equals(secondStr);
Мы говорили о сравнении на соответствие, а теперь разберем сравнение для сортировки. Ведь для сортировки чего-либо нам нужно знать, по какому принципу сортировать. Для этого можно воспользоваться стандартным отсортированным множеством — TreeSet.
Подробнее о различных коллекциях в Java вы можете почитать в этой статье.
Данный список работает на основе алгоритма красно-черного дерева и сортирует множество в соответствии с заданным принципом сортировки.
Как я и сказал ранее, нужно понимать, как отсортировать объекты определенного типа. Чтобы задать способ сравнения для сортировки, используют компараторы.
Как правило их необходимо реализовывать для классов, которые вы хотите сортировать, но в случае со String они уже реализованы.
Поэтому просто добавляем нужные нам строки в TreeSet, а он их отсортирует:
TreeSet<String> sortedSet = new TreeSet<>();
sortedSet.add("B");
sortedSet.add("C");
sortedSet.add("A");
sortedSet.forEach(System.out::println);
Вывод в консоли:
60. Приведите алгоритм перевода строки в символ. Напишите соответствующий код
Как я и говорил ранее, у объектов класса String очень много разных полезных методов. Один из таких — toCharArray. Данный метод переводит строку в массив символов:
String str = "Hello world";
char[] charArr = str.toCharArray();
Далее у нас есть массив символов, которые можем вызывать по индексу:
char firstChar = charArr[0]; // H
61. Как перевести строку в массив байтов и назад ? Напишите соответствующий код
Аналогичный методу toCharArray, класс String имеет метод getBytes, который возвращает массив байтов строки:
String str = "Hello world";
byte[] byteArr = str.getBytes();
byte firstChar = byteArr[6]; // 119
Сегодняшняя часть разбора подошла к логическому концу. Спасибо за внимание!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ