Нажмите ★, если вам нравится проект. Ваш вклад сердечно ♡ приветствуется.
Если вам интересно мое резюме: https://github.com/DEBAGanov
SQL, JDBC (оглавление)
- Cобеседование по Java. Разбор вопросов и ответов.
- JDBC
- Что такое ORM?
- Что такое JDBC?
- Что такое JPA?
- Различия JPA JDBC и Hibernate?
- Что такое JPQL и HQL?
- Criteria API
- В чем заключаются преимущества использования JDBC?
- В чем заключаются преимущества использования Hibernate?
- Что из себя представляет JDBC URL?
- Из каких частей стоит JDBC?
- Перечислите основные классы и интерфейсы JDBC.
- Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?
- Опишите основные этапы работы с базой данных при использовании JDBC.
- Как зарегистрировать драйвер JDBC?
- Как установить соединение с базой данных?
- Транзакции в Hibernate.
- Какие уровни изоляции транзакций поддерживаются в JDBC?
- При помощи чего формируются запросы к базе данных?
- Чем отличается Statement от PreparedStatement?
- Как осуществляется запрос к базе данных и обработка результатов?
- Как вызвать хранимую процедуру?
- Как закрыть соединение с базой данных?
- Что такое Entity?
- Может ли абстрактный класс быть Entity?
- Как наследуется Entity?
- Что такое POJO-класс?
- Какие типы данных можно использовать в атрибутах, входящих в первичный ключ Entity класса (составной или простой), чтобы полученный первичный ключ мог использоваться для любой базы данных? А в случае автогенерируемого первичного ключа (generated primary keys)?
- Что такое встраиваемый (Embeddable) класс?
- Может ли встраиваемый (Embeddable) класс содержать другой встраиваемый (Embeddable) класс?
- Может ли встраиваемый (Embeddable) класс содержать связи (relationship) с другими Entity или коллекциями Entity? Если может, то существуют ли какие-то ограничение на такие связи (relationship)?
- Какие требования JPA устанавливает к встраиваемым (Embeddable) классам?
- Что такое Mapped Superclass?
- Mapped Superclass vs. Embeddable class
- Основные классы и интерфейсы JPA
- Что такое Метамодель?
- Основные классы и интерфейсы Hibernate?
- Способы сконфигурировать Hibernate?
- SessionFactory vs EntityManagerFactory и Session vs EntityManager?
- Жизненный цикл Entity?
- Влияние операций EntityManager на Entity объекты различный жизненных циклов?
- Аннотации JPA
- Аннотации Hibernate
- Кеширование в Hibernate?
- Для чего нужна аннотация @Cacheable?
- Hibernate proxy (lazy load).
- Стратегии кеширования?
- Какие три типа стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA?
- Стратегии загрузки объектов в Hibernate?
- Для чего нужна аннотация Basic?
- Для чего нужна аннотация Column?
- Для чего нужна аннотация Access?
- Для чего нужны аннотации @Embedded и @Embeddable?
- Для чего нужны аннотации @OrderBy и @OrderColumn
- Для чего нужны callback методы в JPA? К каким сущностям применяются аннотации callback методов? Перечислите семь callback методов (или, что тоже самое, аннотаций callback методов)
- Какие видов блокировок (lock) описаны в спецификации JPA?
- Как можно изменить настройки fetch стратегии любых атрибутов Entity для отдельных запросов (query) или методов поиска (find), то если у Enity есть атрибут с fetchType = LAZY, но для конкретного запроса его требуется сделать EAGER или наоборот?
- Что означает полиморфизм (polymorphism) в запросах JPQL (Java Persistence query language) и как его «выключить»?
- В чем разница в требованиях к Entity в Hibernate, от требований к Entity, указанных в спецификации JPA
- Каскадирование
- Как определить владельца связи?
- Что происходит с таблицами при изпользовании mappedBy?
- FetchType стратегии по-умолчанию?
- Enum значения в БД?
- Как мапятся даты (до Java 8 и после)?
- PersistenceContext
- Entity graph
- Пробелма n+1 select и как ее решить?
- JOIN FETCH vs Join
- Criteria Api
- OrderBy vs OrderColumn
- Как создать составной ключ
- GeneratedValue
- OrphanRemoval vs cascadeType.remove
- ElementCollection - Как сохранять в БД коллекции базовых типов?
- Метод unWrap()
- Источники
ORM (Object-relational mapping) — технология программирования, которая позволяет преобразовывать несовместимые типы моделей в ООП, в частности, между хранилищем данных и объектами программирования. ORM используется для упрощения процесса сохранения объектов в реляционную базу данных и их извлечения, при этом ORM сама заботится о преобразовании данных между двумя несовместимыми состояниями.
JDBC, Java DataBase Connectivity (соединение с базами данных на Java) — промышленный стандарт взаимодействия Java-приложений с различными СУБД. Реализован в виде пакета java.sql
, входящего в состав Java SE.
JDBC основан на концепции драйверов, которые позволяют получать соединение с базой данных по специально описанному URL. При загрузке драйвер регистрирует себя в системе и в дальнейшем автоматически вызывается, когда программа требует URL, содержащий протокол, за который этот драйвер отвечает.
JPA (Java Persistence API) это спецификация Java EE и Java SE, описывающая систему управления сохранением java объектов в таблицы реляционных баз данных в удобном виде. Сама Java не содержит реализации JPA, однако есть существует много реализаций данной спецификации от разных компаний.
JDBC является гораздо более низкой (и более старой) спецификацией, чем JPA. JDBC - это API-интерфейс для взаимодействия с базой данных с использованием чистого SQL - отправки запросов и получения результатов. Он не имеет понятия об объектах или иерархиях. При использовании JDBC вам необходимо преобразовать набор результатов в объекты Java.
А в JPA (который использует JDBC снизу) вы также указываете эти детали метаданных базы данных, но с использованием аннотаций Java. Таким образом, JPA создает запросы на обновление для вас и управляет объектами, которые вы искали или создали / обновили (это также делает больше).
Hibernate одна из самых популярных открытых реализаций последней версии спецификации. То есть JPA только описывает правила и API, а Hibernate реализует эти описания.
- 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:
Query query = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name"
);
TypedQuery<Person> 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<Person> 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.
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" );
__Начиная с версии 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 нельзя. Критерии используются только для выборки из базы данных в более объектно-ориентированном стиле. Используется для динамических запросов. Запросы выглядят так:
session.createCriteria(Person.class)
.setMaxResults(10)
.list()
.forEach(System.out::println);
Запрос выше полностью аналогичен запросу HQL "from Person". С Criteria также работают и все те вещи, которые работают и с Query: пейджинг, таймауты и т.д.
Разумеется, в Criteria запросах можно и нужно накладывать условия, по которым объекты будут отбираться:
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:
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> personCriteria = cb.createQuery(Person.class);
Root<Person> 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 могут отличаться:
CriteriaQuery<Passport> passportCriteria = cb.createQuery(Passport.class);
Root<Person> 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 - развалится этот запрос:
CriteriaQuery<Person> personWorkCriteria = cb.createQuery(Person.class);
Root<Person> personWorkRoot = personWorkCriteria.from(Person.class);
Join<Person, Company> 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 класс выглядит примерно вот так:
@StaticMetamodel(Company.class)
public abstract class Company_ extends AbstractIdentifiableObject_ {
public static volatile SingularAttribute<Company, String> name;
public static volatile CollectionAttribute<Company, Person> workers;
}
В Metamodel классе описываются, какие поля присутствуют в сущности, какого они типа, коллекция это или нет и т.д. Для каждой сущности создаётся свой класс Metаmodel.
Создаются классы Metamodel разумеется не вручную. То есть можно их и вручную создать, но тогда пропадает автоматичность проверки и теряется смысл всей этой затеи. Обычно же классы Metamodel генерируются на этапе компиляции тем или иным методом. Конкретная реализация генерации зависит от конкретной реализации JPA и может меняться
Преимуществами JDBC считают:
- Лёгкость разработки: разработчик может не знать специфики базы данных, с которой работает;
- Код практически не меняется, если компания переходит на другую базу данных (количество изменений зависит исключительно от различий между диалектами SQL);
- Не нужно дополнительно устанавливать клиентскую программу;
- К любой базе данных можно подсоединиться через легко описываемый URL.
Hibernate является одним из самых востребованных ORM фреймворков для Java. И вот почему:
- Hibernate устраняет множество спагетти кода (повторяющегося), который постоянно преследует разработчика при работе с JDBC. Скрывает от разработчика множество кода, необходимого для управления ресурсами и позволяет сосредоточиться на бизнес логике.
- Hibernate поддерживает XML так же как и JPA аннотации, что позволяет сделать реализацию кода независимой.
- Hibernate предоставляет собственный мощный язык запросов (HQL), который похож на SQL. Стоит отметить, что HQL полностью объектно-ориентирован и понимает такие принципы, как наследование, полиморфизм и ассоциации (связи).
- Hibernate легко интегрируется с другими Java EE фреймворками, например, Spring Framework поддерживает встроенную интеграцию с Hibernate.
- Hibernate поддерживает ленивую инициализацию используя proxy объекты и выполняет запросы к базе данных только по необходимости.
- Hibernate поддерживает разные уровни cache, а следовательно может повысить производительность.
- Важно, что Hibernate может использовать чистый SQL, а значит поддерживает возможность оптимизации запросов и работы с любым сторонним вендором БД и его фичами.
Hibernate имеет ряд преимуществ перед JDBC API:
- Hibernate удаляет множество повторяющегося кода из JDBC API, а следовательно его легче читать, писать и поддерживать.
- Hibernate поддерживает наследование, ассоциации и коллекции, что не доступно в JDBC API.
- Hibernate неявно использует управление транзакциями. Большинство запросов нельзя выполнить вне транзакции. При использовании JDBC API для управления транзакциями нужно явно использовать commit и rollback.
- JDBC API throws SQLException, которое относится к проверяемым исключениям, а значит необходимо постоянно писать множество блоков try-catch. В большинстве случаев это не нужно для каждого вызова JDBC и используется для управления транзакциями. Hibernate оборачивает исключения JDBC через непроверяемые JDBCException или HibernateException, а значит нет необходимости проверять их в коде каждый раз. Встроенная поддержка управления транзакциями в Hibernate убирает блоки try-catch.
- Hibernate Query Language (HQL) более объектно ориентированный и близкий к Java язык запросов, чем SQL в JDBC.
- Hibernate поддерживает кэширование, а запросы JDBC — нет, что может понизить производительность.
- Hibernate поддерживает аннотации JPA, а значит код является переносимым на другие ORM фреймворки, реализующие стандарт, в то время как код JDBC сильно привязан к приложению.
JDBC URL состоит из:
<protocol>:
(протокола) - всегдаjdbc:
.<subprotocol>:
(подпротокола) - это имя драйвера или имя механизма соединения с базой данных. Подпротокол может поддерживаться одним или несколькими драйверами. Лежащий на поверхности пример подпротокола - это "odbc", отведенный для URL, обозначающих имя источника данных ODBC. В случае необходимости использовать сервис имен (т.е. имя базы данных в JDBC URL не будет действительным именем базы данных), то подпротоколом может выступать сервис имен.<subname>
(подимени) - это идентификатор базы данных. Значение подимени может менятся в зависимости от подпротокола, и может также иметь под-подимя с синтаксисом, определяемым разработчиком драйвера. Назначение подимени - это предоставление всей информации, необходимой для поиска базы данных. Например, если база данных находится в Интернет, то в состав подимени JDBC URL должен быть включен сетевой адрес, подчиняющийся следующим соглашениям://<hostname>:<port>/<subsubname
.
Пример JDBC URL для подключения к MySQL базе данных «Test» расположенной по адресу localhost и ожидающей соединений по порту 3306: jdbc:mysql://localhost:3306/Test
JDBC состоит из двух частей:
- JDBC API, который содержит набор классов и интерфейсов, определяющих доступ к базам данных. Эти классы и методы объявлены в двух пакетах -
java.sql
иjavax.sql
; - JDBC-драйвер, компонент, специфичный для каждой базы данных.
JDBC превращает вызовы уровня API в «родные» команды того или иного сервера баз данных.
-
java.sql.DriverManager
- позволяет загрузить и зарегистрировать необходимый JDBC-драйвер, а затем получить соединение с базой данных. -
javax.sql.DataSource
- решает те же задачи, что и DriverManager, но более удобным и универсальным образом. Существуют такжеjavax.sql.ConnectionPoolDataSource
иjavax.sq1.XADataSource
задача которых - обеспечение поддержки пула соединений. -
java.sql.Connection
- обеспечивает формирование запросов к источнику данных и управление транзакциями. Также предусмотрены интерфейсыjavax.sql.PooledConnection
иjavax.sql.XAConnection
. -
java.sql.Statement
,java.sql.PreparedStatement
иjava.sql.CallableStatement
- эти интерфейсы позволяют отправить запрос к источнику данных. -
java.sql.ResultSet
- объявляет методы, которые позволяют перемещаться по набору данных и считывать значения отдельных полей в текущей записи. -
java.sql.ResultSetMetaData
- позволяет получить информацию о структуре набора данных. -
java.sql.DatabaseMetaData
- позволяет получить информацию о структуре источника данных.
JDBC Type | Java Object Type |
---|---|
CHAR | String |
VARCHAR | String |
LONGVARCHAR | String |
NUMERIC | java.math.BigDecimal |
DECIMAL | java.math.BigDecimal |
BIT | Boolean |
TINYINT | Integer |
SMALLINT | Integer |
INTEGER | Integer |
BIGINT | Long |
REAL | Float |
FLOAT | Double |
DOUBLE | Double |
BINARY | byte[] |
VARBINARY | byte[] |
LONGVARBINARY | byte[] |
DATE | java.sql.Date |
TIME | java.sql.Time |
TIMESTAMP | java.sql.Timestamp |
CLOB | Clob |
BLOB | Blob |
ARRAY | Array |
STRUCT | Struct |
REF | Ref |
DISTINCT | сопоставление базового типа |
JAVA_OBJECT | базовый класс Java |
- Регистрация драйверов;
- Установление соединения с базой данных;
- Создание запроса(ов) к базе данных;
- Выполнение запроса(ов) к базе данных;
- Обработка результата(ов);
- Закрытие соединения с базой данных.
Регистрацию драйвера можно осуществить несколькими способами:
-
java.sql.DriverManager.registerDriver(%объект класса драйвера%)
. -
Class.forName(«полное имя класса драйвера»).newInstance()
. -
Class.forName(«полное имя класса драйвера»)
;
Для установки соединения с базой данных используется статический вызов java.sql.DriverManager.getConnection(...)
.
В качестве параметра может передаваться:
- URL базы данных
static Connection getConnection(String url)
- URL базы данных и набор свойств для инициализации
static Connection getConnection(String url, Properties info)
- URL базы данных, имя пользователя и пароль
static Connection getConnection(String url, String user, String password)
В результате вызова будет установлено соединение с базой данных и создан объект класса java.sql.Connection
- своеобразная «сессия», внутри контекста которой и будет происходить дальнейшая работа с базой данных.
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/
Уровень изолированности транзакций — значение, определяющее уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность данных, но при этом может снижаться количество параллельно выполняемых транзакций. С другой стороны, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных.
Во время использования транзакций, для обеспечения целостности данных, СУБД использует блокировки, чтобы заблокировать доступ других обращений к данным, участвующим в транзакции. Такие блокировки необходимы, чтобы предотвратить:
-
«грязное» чтение (dirty read) — чтение данных, добавленных или изменённых транзакцией, которая впоследствии не подтвердится (откатится);
-
неповторяющееся чтение (non-repeatable read) — при повторном чтении в рамках одной транзакции ранее прочитанные данные оказываются изменёнными;
-
фантомное чтение (phantom reads) — ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк.
Уровни изоляции транзакций определены в виде констант интерфейса java.sql.Connection
:
-
TRANSACTION_NONE
– драйвер не поддерживает транзакции; -
TRANSACTION_READ_UNCOMMITTED
– позволяет транзакциям видеть несохраненные изменения данных: разрешает грязное, непроверяющееся и фантомное чтения; -
TRANSACTION_READ_COMMITTED
– любое изменение, сделанное в транзакции, не видно вне неё, пока она не сохранена: предотвращает грязное чтение, но разрешает непроверяющееся и фантомное; -
TRANSACTION_REPEATABLE_READ
– запрещает грязное и непроверяющееся, фантомное чтение разрешено; -
TRANSACTION_SERIALIZABLE
– грязное, непроверяющееся и фантомное чтения запрещены.
NB! Сервер базы данных может не поддерживать все уровни изоляции. Интерфейс
java.sql.DatabaseMetaData
предоставляет информацию об уровнях изолированности транзакций, которые поддерживаются данной СУБД.
Уровень изоляции транзакции используемый СУБД можно задать с помощью метода setTransactionIsolation()
объекта java.sql.Connection
. Получить информацию о применяемом уровне изоляции поможет метод getTransactionIsolation()
.
Для выполнения запросов к базе данных в Java используются три интерфейса:
java.sql.Statement
- для операторов SQL без параметров;java.sql.PreparedStatement
- для операторов SQL с параметрами и часто выполняемых операторов;java.sql.CallableStatement
- для исполнения хранимых в базе процедур.
Объекты-носители интерфейсов создаются при помощи методов объекта java.sql.Connection
:
java.sql.createStatement()
возвращает объект Statement;java.sql.prepareStatement()
возвращает объект PreparedStatement;java.sql.prepareCall()
возвращает объект CallableStatement;
- Statement: используется для простых случаев запроса без параметров.
- PreparedStatement: предварительно компилирует запрос, который может содержать входные параметры и выполняться несколько раз с разным набором этих параметров.
Перед выполнением СУБД разбирает каждый запрос, оптимизирует его и создает «план» (query plan) его выполнения. Если один и тот же запрос выполняется несколько раз, то СУБД в состоянии кэшировать план его выполнения и не производить этапов разборки и оптимизации повторно. Благодаря этому запрос выполняется быстрее.
Суммируя: PreparedStatement выгодно отличается от Statement тем, что при повторном использовании с одним или несколькими наборами параметров позволяет получить преимущества заранее прекомпилированного и кэшированного запроса, помогая при этом избежать SQL Injection.
Выполнение запросов осуществляется при помощи вызова методов объекта, реализующего интерфейс java.sql.Statement
:
-
executeQuery()
- для запросов, результатом которых является один набор значений, например запросовSELECT
. Результатом выполнения является объект классаjava.sql.ResultSet
; -
executeUpdate()
- для выполнения операторовINSERT
,UPDATE
илиDELETE
, а также для операторов DDL (Data Definition Language). Метод возвращает целое число, показывающее, сколько записей было модифицировано; -
execute()
– исполняет SQL-команды, которые могут возвращать различные результаты. Например, может использоваться для операцииCREATE TABLE
. Возвращаетtrue
, если первый результат содержит ResultSet иfalse
, если первый результат - это количество модифицированных записей или результат отсутствует. Чтобы получить первый результат необходимо вызвать методgetResultSet()
илиgetUpdateCount()
. Остальные результаты доступны через вызовgetMoreResults()
, который при необходимости может быть произведён многократно.
Объект с интерфейсом java.sql.ResultSet
хранит в себе результат запроса к базе данных - некий набор данных, внутри которого есть курсор, указывающий на один из элементов набора данных - текущую запись.
Используя курсор можно перемещаться по набору данных при помощи метода next()
.
NB! Сразу после получения набора данных его курсор находится перед первой записью и чтобы сделать её текущей необходимо вызвать метод
next()
.
Содержание полей текущей записи доступно через вызовы методов getInt()
, getFloat()
, getString()
, getDate()
и им подобных.
Хранимые процедуры – это именованный набор операторов SQL хранящийся на сервере. Такую процедуру можно вызвать из Java-класса с помощью вызова методов объекта реализующего интерфейс java.sql.Statement
.
Выбор объекта зависит от характеристик хранимой процедуры:
- без параметров →
Statement
- с входными параметрами →
PreparedStatement
- с входными и выходными параметрами →
CallableStatement
Если неизвестно, как была определена хранимая процедура, для получения информации о хранимой процедуре (например, имен и типов параметров) можно использовать методы
java.sql.DatabaseMetaData
позволяющие получить информацию о структуре источника данных.
Пример вызова хранимой процедуры с входными и выходными параметрами:
public vois runStoredProcedure(final Connection connection) throws Exception {
// описываем хранимую процедуру
String procedure = "{ call procedureExample(?, ?, ?) }";
// подготавливаем запрос
CallableStatement cs = connection.prepareCall(procedure);
// устанавливаем входные параметры
cs.setString(1, "abcd");
cs.setBoolean(2, true);
cs.setInt(3, 10);
// описываем выходные параметры
cs.registerOutParameter(1, java.sql.Types.VARCHAR);
cs.registerOutParameter(2, java.sql.Types.INTEGER);
// запускаем выполнение хранимой процедуры
cs.execute();
// получаем результаты
String parameter1 = cs.getString(1);
int parameter2 = cs.getInt(2);
// заканчиваем работу с запросом
cs.close();
}
Соединение с базой данной закрывается вызовом метода close()
у соответствующего объекта java.sql.Connection
или посредством использования механизма try-with-resources при создании такого объекта, появившегося в Java 7.
NB! Предварительно необходимо закрыть все запросы созданные этим соединением.
Entity это легковесный хранимый объект бизнес логики (persistent domain object). Основная программная сущность это entity класс, который так же может использовать дополнительные классы, который могут использоваться как вспомогательные классы или для сохранения состояния еntity.
При этом у одной и той же табличке в БД может быть несколько Entity.
Через аннотации можно указать, как работать со свойствами классов (property). Через методы, когда аннотации стоят над методами (геттерами и сеттерами), либо когда аннотации стоят над полями (через Reflection), то есть переменными класса (instance variables). Соответственно, при этом тип доступа будет либо property access или field access. Оба типа элементов Entity класса называются атрибутами Entity класса.
Допустимые типы атрибутов у Entity классов:
- примитивные типы и их обертки Java,
- строки,
- любые сериализуемые типы Java (реализующие Serializable интерфейс),
- enums;
- entity types;
- embeddable классы
- и коллекции типов 1-6
Требования JPA к Entity классам:
- Entity класс должен быть отмечен аннотацией Entity или описан в XML файле конфигурации JPA,
- Entity класс должен содержать public или protected конструктор без аргументов (он также может иметь конструкторы с аргументами),
- Entity класс должен быть классом верхнего уровня (top-level class),
- Entity класс не может быть enum или интерфейсом,
- Entity класс не может быть финальным классом (final class),
- Entity класс не может содержать финальные поля или методы, если они участвуют в маппинге (persistent final methods or persistent final instance variables),
- Если объект Entity класса будет передаваться по значению как отдельный объект (detached object), например через удаленный интерфейс (through a remote interface), он так же должен реализовывать Serializable интерфейс,
- Поля Entity класс должны быть напрямую доступны только методам самого Entity класса и не должны быть напрямую доступны другим классам, использующим этот Entity. Такие классы должны обращаться только к методам (getter/setter методам или другим методам бизнес-логики в Entity классе),
- Enity класс должен содержать первичный ключ, то есть атрибут или группу атрибутов которые уникально определяют запись этого Enity класса в базе данных,
Требования Hibernate к Entity классам:
- Класс сущности должен иметь конструктор без аргументов, который может быть не только public или protected, но и package visibility (default).
- Класс сущности не обязательно должен быть классом верхнего уровня.
- Технически Hibernate может сохранять финальные классы или классы с финальными методами (getter / setter). Однако, как правило, это не очень хорошая идея, так как это лишит Hibernate возможности генерировать прокси для отложенной загрузки сущности.
- Hibernate не запрещает разработчику приложения открывать прямой доступ к переменным экземпляра и ссылаться на них извне класса сущности. Однако обоснованность такого подхода спорна.
Абстрактный класс может быть Entity классом. Абстрактный Entity класс отличается от обычных Entity классов только тем, что нельзя создать объект этого класса. Имена абстрактных классов могут использоваться в запросах.
Абстрактные Entity классы используются в наследовании, когда их потомки наследуют поля абстрактного класса:
@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;
.............
}
- Может наследоваться и от других Entity классов, и от не Entity классов. Состояние (поля) не Entity суперкласса не является персистентным, то есть не хранится в БД и не обрабатывается провайдером (Hibernate), поэтому любое такое состояние (поля), унаследованное Entity классом, также не будет отображаться в БД. Не Entity суперклассы не могут участвовать в операциях EntityManager или Query. Любые маппинги или аннотации отношений в не Entity суперклассах игнорируются.
- Не Entity классы так же могут наследоваться от Entity.
- Может быть абстрактным, при этом он сохраняет все свойства Entity, за исключением того что его нельзя непосредственно инициализировать.
Plain Old Java Object - простой Java-объект, не унаследованный от какого-то специфического объекта и не реализующий никаких служебных интерфейсов сверх тех, которые нужны для бизнес-модели.
Какие типы данных можно использовать в атрибутах, входящих в первичный ключ Entity класса (составной или простой), чтобы полученный первичный ключ мог использоваться для любой базы данных? А в случае автогенерируемого первичного ключа (generated primary keys)?
Допустимые типы атрибутов, входящих в первичный ключ:
- примитивные типы и их обертки Java,
- строки,
- BigDecimal и BigInteger,
- java.util.Date и java.sql.Date
В случае автогенерируемого первичного ключа (generated primary keys) допустимы только числовые типы.
В случае использования других типов данных в первичном ключе, он может работать только для некоторых баз данных, т.е. становится не переносимым (not portable).
Встраиваемый (Embeddable) класс это класс который не используется сам по себе, только как часть одного или нескольких Entity классов.
Например, у нас может быть встраиваемый класс ClassA, который представляет собой композицию строкового и числового значений, и эти два поля будут добавлены в класс EntityA:
@Entity
public class EntityA {
@Id
@GeneratedValue
private int id;
@Embedded
private ClassA classARef;
.............
}
@Embeddable
public class ClassA {
private String myStr;
private int myInt;
.............
}
Entity класс могут содержать как одиночные встраиваемые классы, так и коллекции таких классов. Также такие классы могут быть использованы как ключи или значения map. Во время выполнения каждый встраиваемый класс принадлежит только одному объекту Entity класса и не может быть использован для передачи данных между объектами Entity классов (то есть такой класс не является общей структурой данных для разных объектов). В целом, такой класс служит для того чтобы выносить определение общих атрибутов для нескольких Entity, можно считать что JPA просто встраивает в Entity вместо объекта такого класса те атрибуты, которые он содержит. То есть, если класс Person с полями name и age встроен и в класс Driver, и в класс Baker, то у обоих последних классов появятся оба поля из класса Person. Но если у объекта Driver эти поля будут иметь значения “Иван” и “35”, то эти же поля у объекта Baker могут иметь совершенно иные значения, никак не связанные с объектом Driver.
Особенности встраиваемых классов
- все поля встраиваемого класса, даже коллекции, станут полями класса, в который происходит встраивание;
- встраиваемые классы могут быть встроены в одну и ту же сущность несколько раз, нужно только поменять имена полей;
- экземпляры встраиваемых классов, в отличие от экземпляров сущностей, не имеют собственного персистентного состояния, вместо этого они существуют только как часть состояния объекта, которому они принадлежат;
- встраиваемые классы могут использовать в качестве полей: ➢ базовые типы; ➢ коллекции базовых типов (с аннотацией @ElementCollection); ➢ другие встраиваемые классы; ➢ коллекции других встраиваемых классов (с аннотацией @ElementCollection); ➢ сущности; ➢ коллекции сущностей;
- сущность может использовать в качестве полей одиночные встраиваемые классы и коллекции встраиваемых классов;
- встраиваемые классы могут использоваться в качестве ключей и значений Map.
Так как мы можем встраивать классы в неограниченное количество других классов, то у каждого класса, содержащего встраиваемый класс, мы можем изменить названия полей из встраиваемого класса. Например, у класса Driver поля из встраиваемого класса Person будут изменены с name на driver_name и с age на driver_age
@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, но не обязаны это делать.
Можно использовать для денормализации БД (ускорений запросов к БД)
Да, может.
Может ли встраиваемый (Embeddable) класс содержать связи (relationship) с другими Entity или коллекциями Entity? Если может, то существуют ли какие-то ограничение на такие связи (relationship)?
Может, но только в случае если такой класс не используется как первичный ключ или ключ map’ы.
- Такие классы должны удовлетворять тем же правилам что Entity классы, за исключением того что они не обязаны содержать первичный ключ и быть отмечены аннотацией Entity (см. вопрос 10),
- Embeddable класс должен быть отмечен аннотацией Embeddable или описан в XML файле конфигурации JPA.
Mapped Superclass это класс от которого наследуются Entity, он может содержать аннотации JPA, однако сам такой класс не является Entity, ему не обязательно выполнять все требования установленные для Entity (например, он может не содержать первичного ключа). Такой класс не может использоваться в операциях EntityManager или Query. Такой класс должен быть отмечен аннотацией MappedSuperclass или соответственно описан в xml файле.
Особенности ‒ Должен быть помечен аннотацией @MappedSuperclass или описан в xml файле. ‒ Не может использоваться в операциях EntityManager или Query, вместо этого нужно использовать классы-наследники. ‒ Не может состоять в отношениях с другими сущностями (в сущности нельзя создать поле с типом сопоставленного суперкласса). ‒ Может быть абстрактным. ‒ Не имеет своей таблицы в БД.
Для того, чтобы использовать Mapped Superclass, достаточно унаследовать его в классах-потомках:
@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 не будет:
Это похоже на стратегию наследования “Таблица для каждого конкретного класса сущностей”, но в модели данных нет объединения таблиц или наследования. Также тут нет таблицы для Mapped Superclass. Наследование существует только в объектной модели.
Основным недостатком использования сопоставленного суперкласса является то, что полиморфные запросы невозможны, то есть мы не можем загрузить всех наследников Mapped Superclass.
Сходства:
- не являются сущностями и могут иметь все аннотации, кроме @Entity;
- не имеют своих таблиц в БД;
- не могут использоваться в операциях EntityManager или Query.
Различия:
- MappedSuperclass - наследование, Embeddable class - композиция (экземпляр «части» может входить только в одно целое (или никуда не входить));
- поля из Mapped Superclass могут быть у сущности в одном экземпляре, полей из Embeddable class может быть сколько угодно (встроив в сущность Embeddable class несколько раз и поменяв имена полей);
- в сущности нельзя создать поле с типом сопоставленного суперкласса, а с Embeddable можно и нужно.
EntityManagerFactory – фабричный класс EntityManager. Он создает и управляет несколькими экземплярами EntityManager. Создание EntityManagerFactory довольно дорогая операция, поэтому обычно её создают один раз и на всё приложение.
Методы:
- createEntityManager()
- createEntityManager(Map map) - Create a new application-managed EntityManager with the specified Map of properties.
- close() - Close the factory, releasing any resources that it holds.
- getCache() - Access the cache that is associated with the entity manager factory (the "second level cache").
- getCriteriaBuilder() - Return an instance of CriteriaBuilder for the creation of CriteriaQuery objects.
- getMetamodel() - Return an instance of Metamodel interface for access to the metamodel of the persistence unit.
- getPersistenceUnitUtil() - Return interface providing access to utility methods for the persistence unit.
- getProperties() - Get the properties and associated values that are in effect for the entity manager factory.
- isOpen() - Indicates whether the factory is open.
EntityManager – интерфейс, вызывая методы которого можно управлять сущностями. В отличие от фабрики, достаточно легковесен и поэтому зачастую создаётся по месту использования и в больших количествах. Если проводить аналогию с обычным JDBC, то EntityManagerFactory будет аналогом DataSource, а EntityManager аналогом Connection.
Методы операций над Entity:
- persist (добавление Entity под управление JPA)
- merge (обновление)
- remove (удаления)
- 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.
Entity – это постоянные объекты, хранящиеся в виде записей в базе данных.
EntityTransaction – интерфейс для работы с транзакциями. Он имеет непосредственное отношение к EntityManager. Для каждого EntityManager операции поддерживаются классом EntityTransaction.
Методы:
- begin() - Start a resource transaction.
- commit() - Commit the current resource transaction, writing any unflushed changes to the database.
- getRollbackOnly() - Determine whether the current resource transaction has been marked for rollback.
- isActive() - Indicate whether a resource transaction is in progress.
- rollback() - Roll back the current resource transaction.
- setRollbackOnly() - Mark the current resource transaction so that the only possible outcome of the transaction is for the transaction to be rolled back.
Persistence – Этот класс содержит статические методы для получения экземпляра EntityManagerFactory.
Query – Этот интерфейс реализуется каждым поставщиком JPA для получения реляционных объектов, соответствующих критериям.
Для получения метаданные JPA (сведения о Entity типах, о полях сущности, Embeddable и Managed классах и т.п. использую Рефлексию) используется интерфейс Metamodel. Объект этого интерфейса можно получить методом getMetamodel у EntityManagerFactory или EntityManager.
SessionFactory – используется для получения объектов Session, которые используются для операций с базами данных. SessionFactory отвечает за считывание параметров конфигурации Hibernate и подключение к базе данных. Неизменяемый потокобезопасный объект с компилированным маппингом для одной базы данных. Необходимо инициализировать SessionFactory всего один раз.
Session – однопоточный короткоживущий объект, который предоставляет связь между объектами приложения и базой данных. Он оборачивает JDBC java.sql.Connection и работает как фабрика для org.hibernate.Transaction. Разработчик должен открывать сессию по необходимости и закрывать ее сразу после использования. Экземпляр Session является интерфейсом между кодом в java приложении и hibernate framework и предоставляет методы для операций CRUD. Расширяет интерфейс EntityManager
Transaction – однопоточный короткоживущий объект, используемый для атомарных операций. Это абстракция приложения от основных JDBC или JTA транзакций. org.hibernate.Session может занимать несколько org.hibernate.Transaction в определенных случаях.
Configuration - этот объект используется для создания объекта SessionFactory и конфигурирует сам Hibernate
Criteria - Используется для создания и выполнения объекто-ориентированных запроса на получение объектов.
Query - Этот объект использует HQL или SQL для чтения/записи данных из/в БД. Экземпляр запроса используется для связывания парметров запроса, ограничения количества результатов, которые будут возвращены и для выполнения запроса.
- используя аннотации
- 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 аннотации:
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
Вы можете легко получить доступ к базовому SessionFactory используя unwrap метод: SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); То же самое можно сделать с JPA EntityManager. Если вы вводите EntityManager через @PersistenceContext аннотацию:
@PersistenceContext
private EntityManager entityManager;
Вы можете легко получить доступ к базовому, Session используя unwrap метод:
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
Hibernate появился раньше JPA. К этому времени у HIBERNATE был большой наработанный функционал, а в для первой версии JPA удалось согласовать только часть этого объема функционала. Поэтому, разработчики HIBERNATE сознательно пошли на то, чтобы в HIBERNATE имелось два пути работы - старый путь - нативный HIBERNATE (через интерфейс Session) и новый путь JPA (через интерфейс EntityManager).
Интерфейсы разные, методы как правило имеют одинаковые названия. Различия есть и в других элементах. При этом, функциональность нативного HIBERNATE значительно больше, чем у JPA. И вообще, реализация EntityManager является оберткой (wrap) реализации Session. Класс SessionImpl реализует интерфейс Session, а Session расширяет интерфейс EntityManager.
Если Вы выбираете путь JPA, то всегда имеете возможность быстро перейти, на другие реализации JPA- EclipseLink, OpenJPA, DataNucleus (и узнаете насколько они совместимы).
У Entity объекта существует четыре статуса жизненного цикла:
-
new (Transient) — объект создан, но при этом ещё не имеет сгенерированных первичных ключей и пока ещё не сохранен в базе данных, не привязан к текущему контексту персистентности (к сессии и к кэшу первого уровня). Важно запомнить, что если генерация id для сущности производится базой данных - объекты в состоянии Transient не должны содержать никиаких значений в поле id
-
managed (Persistent) — объект создан, управляется JPA, имеет сгенерированные первичные ключи. В это состояние может попасть объект из абсолютно любого состояния. Что бы перевести объекты из состояния Transient в Persistenr - надо воспользоваться методом persist. Для перевода Detached объекта в это состояние - надо вызвать метод merge. И если ассоциированная с объектом запись была удалена, и объект находится в состоянии Removed - так же стоит вопсользоваться методом persist, чтобы снова сохранить данные в базу, а объект перевести в состояния Persist.
-
detached — объект был создан, но не управляется (или больше не управляется) JPA. Объект, который до этого был привязан к контексту персистентности, но теперь отделен от него. Отделение могло произойти по двум причинам: контекст персистентности был закрыт (закончилась тарзакция, закрылась сессия), либо объект был явно отделен методом detach или clear от ещё существующего контекста. Подобный объект имеет заполненное поле id, сгенерированное базой, но контекст персистентности больше не следит за изменением этого объекта. Что бы снова присоединить такой объект к контексту персистентности, и перевести его снова в Persistent надо вызвать метод merge, который сравнит состояние текущего detached объекта и данные из базы
-
removed — объект создан, управляется JPA, но будет удален после commit'a транзакции. Объект, ассоциированная с которым запись была удалена из базы. Такой объект так же не отслеживается контекстом персистентности и информация о нем больше не хранится в кэше первого уровня. Такой объект можно только сохранить в базу снова методом persist
DETACHED
. |
/ \ |
detach(entity) | |
clear() | | merge(entity)
close() | |
| \ / find(entityClass, primaryKey)
| ' getReference(entityClass, primaryKey)
createQuery().getResultList()
NEW ———— persist(entity) —————> MANAGED <————— createQuery().getSingleResult() ————— DATA BASE
———————————————— flush() ———————————————————>
. | .
/ \ | / \
| | |
persist(entity) | | remove(entity) |
| | |
| \ / |
| ' |
REMOVED —————————————————————— flush() ———————————————————
Persist
- New -> меняется на managed и объект будет сохранен в базу при commit'е транзакции или в результате flush операций,
- Managed -> операция игнорируется, однако зависимые Entity могут поменять статус на managed, если у них есть аннотации каскадных изменений,
- Removed -> то он меняется на managed,
- Detached -> будет выкинут exception сразу или на этапе commit'а транзакции,
Remove
- New -> операция игнорируется, однако зависимые Entity могут поменять статус на removed, если у них есть аннотации каскадных изменений и они имели статус managed,
- Managed -> статус меняется на removed и запись объект в базе данных будет удалена при commit'е транзакции (так же произойдут операции remove для всех каскадно зависимых объектов),
- Removed -> операция игнорируется,
- Detached -> будет выкинут exception сразу или на этапе commit'а транзакции,
Merge
- Detached -> то либо данные будет скопированы в существующей managed entity с тем же первичным ключом, либо создан новый managed в который скопируются данные,
- New -> будет создана новый managed entity, в который будут скопированы данные прошлого объекта,
- Managed -> операция игнорируется, однако операция merge сработает на каскадно зависимые Entity, если их статус не managed,
- Removed -> будет выкинут exception сразу или на этапе commit'а транзакции,
Refresh
- Managed -> в результате операции будут востановлены все изменения из базы данных данного Entity, так же произойдет refresh всех каскадно зависимых объектов,
- Если статус new, removed или detached, будет выкинут exception,
Detach
- Managed или Removed -> в результате операции статус Entity (и всех каскадно-зависимых объектов) станет detached.
- New или Detached -> операция игнорируется,
@Access — аннотация используется для указания типа доступа связанного класса сущности, сопоставленного супер класса или встраиваемого класса или атрибута сущности.
@AssociacionOverride — аннотация используется для переопределения реляционных отношений таких как один к одному, многие к одному, один ко многим, многие ко многим (@OneToOne, @ManyToOne, @OneToMany, @ManyToMany) в классах унаследованных от встраиваемых (embeddable) или сопоставляемых супер классов (mapped superclass).
@AssociacionOverrides — аннотация используется для группировки нескольких аннотаций @AssociacionOverride.
@AttibuteOverride — аннотация используется для переопределения сопоставляемых атрибутов Entity классов унаследованных от встраиваемых (embeddable) или сопоставляемых супер классов (mapped superclass). Подробнее...
@AttibuteOverrides — аннотация используется для группировки нескольких аннотаций @AttributeOverride.
@Basic — аннотация используется для сопоставления базового типа атрибута столбцу таблицы базы данных.
@Cacheable — аннотация используется для определения хранения объекта в кэше второго уровня в зависимости от установленного свойства [shared-cache-mode] в файле persistence.xml. При значении [shared-cache-mode] - ENABLE SELECTIVE - в кэше второго уровня будут храниться только объекты помеченные аннотацией @Cacheable. При значении [shared-cache-mode] - DISABLE SELECTIVE - в кэше второго уровня будут храниться только объекты не помеченные аннотацией @Cacheable.
@CollectionTable — аннотация используется для указания таблицы базы данных, в которой хранятся значения базовой или встраиваемой коллекции типов.
@Column — аннотация используется для указания соответствия между атрибутом базовой сущности Entity класса и столбцом таблицы базы данных. Подробнее...
@ColumnResult — аннотация @ColumnResult используется в сочетании с аннотациями @SqlResultSetMapping или @ConstructorResult для отображения столбца SQL для заданного запроса SELECT.
@ConstructorResult — аннотация используется в сочетании с аннотациями @SqlResultSetMapping для сопоставления столбцов заданного запроса SELECT определенному конструктору объекта.
@Convert — аннотация используется для определения реализации AttributeConverter, используемой для преобразования текущего аннотированного базового атрибута. Если AttributeConverter использует autoApply, все атрибуты сущностей с одним и тем же целевым типом будут автоматически преобразованы.
@Converter — аннотирование используется, чтобы указать, что текущая реализация AttributeConverter аннотации может использоваться в качестве конвертора основных атрибутов JPA. Если атрибуту autoApply присвоено значение true, поставщик JPA автоматически преобразует все базовые атрибуты с тем же типом Java, как определено текущим преобразователем.
@Converts — аннотация используется для группирования нескольких аннотаций @Convert.
@DiscriminatorColumn — аннотация используется для указания имени столбца дискриминатора и типа дискриминатора для стратегий наследования (Inheritance) SINGLE_TABLE и JOINED.
@DisccriminatorValue — аннотация используется для определения того, какое значение столбца дискриминатора используется для отображения текущего аннотированного объекта для стратегий наследования (Inheritance) SINGLE_TABLE и JOINED.
@ElementCollection — аннотация используется для указания коллекции базового или встраиваемого типа.
@Embeddable — аннотация используется для указания встраиваемых типов. Как и базовые типы, встраиваемые типы не имеют никакой идентичности, управляемой их собственностью.
@Embedded — аннотация используется, чтобы указать, что данный атрибут сущности представляет встраиваемый тип.
@EmbeddedId — аннотация используется, чтобы указать, что идентификатор объекта является встраиваемым типом.
@Entity — аннотация используется, чтобы указать, что текущий класс представляет тип сущности - Entity класса. В отличие от базовых и встраиваемых типов, типы сущностей имеют идентичность, а их состояние управляется базовым контекстом Persistence. Подробнее...
@EntityListeners — аннотация используется для указания массива классов слушателя обратного вызова, которые используются текущей аннотированной сущностью.
@EntityResult — аннотация используется с аннотацией @SqlResultSetMapping для сопоставления выбранных столбцов сущности.
@Enumerated — аннотация используется, чтобы указать, что атрибут entity представляет перечислимый тип.
@ExcludeDefaultListeners — аннотация используется, чтобы указать, что текущая аннотированная сущность пропускает вызов любого слушателя по умолчанию.
@ExcludeSuperlassListeners — аннотация используется, чтобы указать, что текущая аннотированная сущность пропускает вызов слушателей, объявленных его суперклассом (классом предком).
@FieldResult — аннотация используется с аннотацией @EntityResult для сопоставления выбранных столбцов полям определенного объекта.
@ForeignKey — аннотация используется для указания связанного внешнего ключа сопоставления @JoinColumn. Аннотация @ForeignKey используется только в том случае, если включен инструмент автоматического создания и корректировки схемы базы данных, и в этом случае аннотация позволяет настроить определение базового внешнего ключа.
@GeneratedValue — аннотация указывает метод генерации значения идентификатора (автоматически генерируется с использованием столбца идентификации, последовательности базы данных или генератора таблиц). Hibernate поддерживает сопоставление @GeneratedValue даже для идентификаторов UUID.
@Id — аннотация указывает идентификатор объекта. Объект должен всегда иметь атрибут идентификатора, который используется при загрузке объекта в данном контексте сохранения.
@IdClass — аннотация используется, если текущий объект определяет составной идентификатор. Отдельный класс инкапсулирует все атрибуты идентификатора, которые зеркалируются текущим сопоставлением объектов.
@Index — аннотация используется для создания индекса базы данных если включен инструмент автоматического создания и корректировки схемы базы данных.
@Inheritance — аннотирование используется для указания стратегии наследования для данной иерархии классов сущностей.
@JoinColumn — аннотация используется для указания столбца FOREIGN KEY, используемого при присоединении к ассоциации объекта или встраиваемой коллекции.
@JoinColumns — аннотация используется для группирования нескольких аннотаций @JoinColumn, которые используются при сопоставлении объектов или встраиваемой коллекции с использованием составного идентификатора.
@JoinTable — аннотация используется для указания таблицы связей между двумя другими таблицами базы данных.
@Lob — аннотация используется, чтобы указать, что текущий аннотированный атрибут объекта представляет большой тип объекта.
@ManyToMany — аннотация используется для указания отношения объектов базы данных «многие-ко-многим».
@ManyToOne — аннотация используется для указания отношения «многие-к-одному» для объектов базы данных.
@MapKey — аннотация используется для указания ключа ассоциации java.util.Map, для которой ключ является либо первичным ключом, либо атрибутом объекта, который представляет значение Map.
@MapKeyClass — аннотация используется для указания ключа ассоциаций типа java.util.Map.
@MapKeyColumn — аннотация используется для указания столбца базы данных, в котором хранится ключ ассоциации java.util.Map, для которой ключ карты является базовым типом.
@MapKeyEnumerated — аннотация используется, чтобы указать, что ключ из ассоциации java.util.Map является Java Enum.
@MapKeyJoinColumn — аннотация используется, чтобы указать, что ключ ассоциации java.util.Map является ассоциацией сущностей. Ключевой столбец карты - это FOREIGN KEY в таблице связей, который также присоединяет таблицу владельца карты к таблице, в которой находится значение Map.
@MapKeyJoinColumns — аннотация используется для группирования нескольких сопоставлений @MapKeyJoinColumn, когда ключ ассоциации java.util.Map использует составной идентификатор.
@MapKeyTemporal — аннотация используется, чтобы указать, что ключом ассоциации java.util.Map является @TemporalType (например, DATE, TIME, TIMESTAMP).
@MappedSuperlass — аннотация используется, чтобы указать, что текущие атрибуты аннотированного типа наследуются любой Entity сущностью класса-наследника. Подробнее...
@MapsId — аннотация используется, чтобы указать, что идентификатор объекта сопоставляется текущей аннотированной ассоциацией @ManyToOne или @OneToOne.
@NamedAttributeNode — аннотация используется для указания каждого индивидуального узла атрибута, который необходимо извлечь с помощью диаграммы сущностей.
@NamedEntityGraph — аннотация используется для указания графа сущностей, который может использоваться запросом сущности, чтобы переопределить план выборки по умолчанию.
@NamedEntityGraphs — аннотация используется для группировки нескольких аннотаций @NamedEntityGraph.
@NamedNativeQueries — аннотация используется для объединения нескольких аннотаций @NamedNativeQuery.
@NamedNativeQuery — аннотация используется для указания нативного SQL-запроса, который впоследствии можно найти по его имени.
@NamedQueries — аннотация используется для группирования нескольких аннотаций @NamedQuery.
@NamedQuery — аннотация используется для указания JPQL-запроса, который впоследствии можно найти по его имени.
@NamedStoredProcedureQueries — аннотация используется для группирования нескольких аннотаций @NamedStoredProcedureQuery.
@NamedStoredProcedureQuery — аннотация используется для указания хранимой процедуры базы данных, которую впоследствии можно найти по его имени.
@NamedSubgraph — аннотация используемая для указания субграфа в графе сущностей.
@OneToMany — аннотация используется для указания отношения объектов базы данных «один ко многим».
@OneToOne — аннотация используется для указания отношения «один к одному» объектов базы данных. Подробнее...
@OrderBy — аннотирование используется для указания атрибутов сущности, используемых для сортировки при получении текущей аннотированной коллекции.
@OrderColumn — аннотирование используется, чтобы указать, что текущий сборник аннотаций должен быть материализован в базе данных.
@PersistenceContext — аннотация используется для указания EntityManager, который необходимо ввести как зависимость.
@PersistenceContexts — аннотация используется для группирования нескольких @PersistenceContext аннотаций.
@PersonistenceProperty — аннотация используется аннотацией @PersistenceContext для объявления свойств провайдера JPA, которые передаются в базовый контейнер при создании экземпляра EntityManager.
@PersonistenceUnit — аннотация используется для указания EntityManagerFactory, которая должна быть введена как зависимость.
@PersonistenceUnits — аннотация используется для группирования нескольких аннотаций @PersistenceUnit.
@PostLoad — аннотация используется для указания метода обратного вызова, который срабатывает после загрузки объекта.
@PostPersist — аннотация используется для указания метода обратного вызова, который срабатывает после сохранения объекта.
@PostRemove — аннотация используется для указания метода обратного вызова, который срабатывает после удаления объекта.
@PostUpdate — аннотация используется для указания метода обратного вызова, который срабатывает после обновления объекта.
@PrePersist — аннотация используется для указания метода обратного вызова, который срабатывает до того, как объект будет сохранен.
@PreRemove — аннотация используется для указания метода обратного вызова, который срабатывает до удаления объекта.
@PreUpdate — аннотация используется для указания метода обратного вызова, который срабатывает до обновления объекта.
@PrimaryKeyJoinColumn — аннотация используется, чтобы указать, что столбец первичного ключа текущего аннотированного объекта также является внешним ключом к некоторому другому объекту (например, таблице базового класса в стратегии наследования JOINED, первичной таблице во вторичном сопоставлении таблиц или родительская таблица в отношении @OneToOne).
@PrimaryKeyJoinColumns — аннотация используется для группирования нескольких аннотаций @PrimaryKeyJoinColumn.
@QueryHint — аннотация используется для указания подсказки поставщика JPA, используемой аннотацией @NamedQuery или аннотацией @NamedNativeQuery.
@SecondaryTable — аннотация используется для указания вторичной таблицы для текущего аннотированного объекта Entity класса.
@SecondaryTables — аннотация используется для группирования нескольких аннотаций @SecondaryTable.
@SequenceGenerator — аннотация используется для указания последовательности базы данных, используемой генератором идентификатора текущего аннотированного объекта.
@SqlResultSetMapping — аннотация используется для указания отображения ResultSet собственного SQL-запроса или хранимой процедуры.
@SqlResultSetMappings — аннотация является аннотацией нескольких групп @SqlResultSetMapping.
@StoredProcedureParameter — аннотация используется для указания параметра @NamedStoredProcedureQuery.
@Table — аннотация используется для указания первичной таблицы текущего аннотированного объекта Entity класса. Подробнее...
@TableGenerator — аннотация используется для указания таблицы базы данных, используемой генератором идентификаторов текущего аннотированного объекта Entity класса.
@Temporal — аннотация используется для указания TemporalType текущего аннотированного атрибута объекта java.util.Date или java.util.Calendar.
@Transient — аннотация используется для указания того, что данный атрибут сущности не должен сохраняться.
@UniqueConstraint — аннотация используется для указания уникального ограничения, которое должно быть включено генератором автоматической схемы для первичной или вторичной таблицы, связанной с текущим аннотированным объектом.
@Version — аннотация используется для указания атрибута версии сущности Entity класса, используемого для оптимистической блокировки.
Hibernate значительно расширяет стандартный набор аннотаций определенных спецификацией Java Persistence API (JPA). Использование предложенного набора аннотаций возможно только с Hibernate, перенос программного кода под другую реализацию JPA спецификации становиться невозможным.
@Any — аннотация используется для определения связи any-to-one, которая может указывать на один из нескольких типов сущностей.
@AnyMetaDef — аннотация используется для предоставления метаданных о сопоставлении @Any или @ManyToAny.
@AnyMetaDefs — аннотация используется для группирования нескольких аннотаций @AnyMetaDef.
@AttributeAccessor — аннотация используется для указания настраиваемой PropertyAccessStrategy. Должно использоваться только для присвоения персонализированной PropertyAccessStrategy. Для типа доступа к свойству / полю должна быть предпочтительной аннотация JPA @Access. Однако, если эта аннотация используется с любым значением = "свойство" или значением = "поле", она будет действовать так же, как соответствующее использование комментария JPA @Access.
@BatchSize — аннотация используется, чтобы указать размер для пакетной загрузки записей ленивой коллекции.
@Cache — аннотация используется для указания CacheConcurrencyStrategy корневого объекта или коллекции.
@Cascade — аннотация используется для применения определенных стратегий Haskernate CascadeType (например, CascadeType.LOCK, CascadeType.SAVE_UPDATE, CascadeType.REPLICATE) в данной ассоциации. Для каскадирования JPA предпочтительнее использовать javax.persistence.CascadeType. При объединении стратегий JPA и Hibernate CascadeType Hibernate объединит оба набора каскадов.
@Check — аннотация используются для указания произвольного ограничения SQL CHECK, которое может быть определено на уровне класса.
@CollectionId — аннотация используется для указания столбца идентификатора для коллекции идентификаторов.
@CollectionType — аннотация используется для указания пользовательского типа коллекции. Коллекция также может аннотироваться @Type, который определяет тип Hibernate для элементов коллекции.
@ColumnDefault — аннотация используется для указания значения DEFAULT DDL, применяемого при использовании генератора автоматической схемы. Такое же поведение может быть достигнуто с помощью атрибута определения комментария JPA @Column.
@Columns —аннотация используются для группировки нескольких комментариев JPA @Column.
@ColumnTransformer — аннотация используется для настройки способа чтения или чтения заданного значения столбца в базе данных.
@ColumnTransformers — аннотация используются для группирования нескольких аннотаций @ColumnTransformer.
@CreationTimestamp — аннотация используется, чтобы указать, что текущий аннотированный временный тип должен быть инициализирован текущим значением временной отметки JVM.
@DiscriminatorFormula — аннотация используется для указания Hibernate @Formula для разрешения значения дискриминатора наследования.
@DisccriminatorOptions — аннотация используется для обеспечения силы и вставки свойств дискриминатора.
@DynamicInsert — аннотация используется, чтобы указать, что оператор SQL INSERT должен быть сгенерирован всякий раз, когда сущность должна быть сохранена. По умолчанию Hibernate использует кэшированный оператор INSERT, который устанавливает все столбцы таблицы. Когда объект аннотируется аннотацией @DynamicInsert, PreparedStatement будет включать только ненулевые столбцы.
@DynamicUpdate — аннотация используется для указания того, что SQL-запрос UPDATE должен генерироваться всякий раз, когда объект модифицируется. По умолчанию Hibernate использует кэшированный оператор UPDATE, который устанавливает все столбцы таблицы. Когда объект аннотируется аннотацией @DynamicUpdate, PreparedStatement будет включать только столбцы, значения которых были изменены.
@Fetch — аннотация используется для указания специфического для Hibernate режима FetchMode (например, JOIN, SELECT, SUBSELECT), используемого для текущей аннотированной связи
@FetchProfile — аннотация используется для указания настраиваемого профиля выборки, аналогичного графику объекта JPA.
@FetchProfile.FetchOverride — аннотация используется в сочетании с аннотацией @FetchProfile и используется для переопределения стратегии выборки конкретной ассоциации объекта.
@FetchProfiles — аннотация используется для группирования нескольких аннотаций @FetchProfile.
@Filter — аннотация используется для добавления фильтров к объекту или целевому объекту коллекции.
@FilterDefs — аннотация используется для определения определения @Filter (имя, условие по умолчанию и типы параметров, если они есть).
@FilterJoinTable — аннотация используется для добавления возможностей @Filter в коллекцию таблиц соединений.
@FilterJoinTables — аннотация используется для группировки нескольких аннотаций @FilterJoinTable.
@Filters — аннотация используется для группировки нескольких аннотаций @Filter.
@Formula — аннотация используется для указания фрагмента SQL, который выполняется, чтобы заполнить заданный атрибут объекта.
@Generated — аннотация используется, чтобы указать, что текущий аннотированный атрибут объекта генерируется базой данных.
@GeneratorType — аннотация используется для предоставления ValueGenerator и GenerationTime для текущего сгенерированного атрибута.
@GenericGenerator — аннотация можно использовать для настройки любого генератора идентификаторов Hibernate.
@GenericGenerators — аннотация используется для группирования нескольких аннотаций @GenericGenerator.
@Immutable — аннотация используется, чтобы указать, что аннотированный объект, атрибут или коллекция является неизменяемым.
@JoinColumnOrFormula — аннотация используется для указания того, что ассоциация сущностей разрешена либо через соединение FOREIGN KEY (например, @JoinColumn), либо с использованием результата данной формулы SQL (например, @JoinFormula).
@JoinColumnsOrFormulas — аннотация используется для группирования нескольких аннотаций @JoinColumnOrFormula.
@JoinFormula — аннотация используется в качестве замены для @JoinColumn, если в ассоциации нет выделенного столбца FOREIGN KEY.
@LazyCollection — аннотация используется для указания поведения отложенной загрузки для данной коллекции. Возможные значения перечисляются перечислением LazyCollectionOption: TRUE - загружайте его, когда запрашивается состояние. FALSE - с готовностью загрузите его. EXTRA - используйте дополнительные запросы при полной загрузке коллекции. Значения TRUE и FALSE устарели, поскольку вы должны использовать атрибут JPA FetchType коллекции @ElementCollection, @OneToMany или @ManyToMany.
@LazyGroup — аннотация используется для указания того, что атрибут entity должен быть выбран вместе со всеми другими атрибутами, принадлежащими к той же группе. Чтобы лениво загружать атрибуты объектов, требуется улучшение байт-кода. По умолчанию все атрибуты, не относящиеся к коллекции, загружаются в одну группу с именем «DEFAULT». Эта аннотация позволяет определять различные группы атрибутов, которые должны быть инициализированы вместе при доступе к одному атрибуту в группе.
@LazyToOne — аннотация используется для указания параметров отложенной загрузки, представленных LazyToOneOption, доступных для ассоциации @OneToOne или @ManyToOne. LazyToOneOption определяет следующие альтернативы: FALSE - срочно загрузите ассоциацию. Этот параметр не нужен, поскольку JPA FetchType.EAGER предлагает такое же поведение. NO_PROXY - эта опция будет лениво получать ассоциацию, возвращая реальный объект сущности. PROXY - эта опция будет лениво извлекать связь при возврате прокси вместо этого.
@ListIndexBase — аннотация используется для указания начального значения для индекса списка, который хранится в базе данных. По умолчанию индексы списка сохраняются начиная с нуля. Обычно используется вместе с @OrderColumn.
@Loader — аннотация используется для переопределения запроса SELECT по умолчанию, используемого для загрузки загрузки объекта.
@ManyToAny — аннотация используется для указания связи «многие-к-одному» при динамическом разрешении целевого типа.
@MapKeyType — аннотация используется для указания типа ключа Map.
@MetaValue — аннотация используется аннотацией @AnyMetaDef для указания связи между заданным значением дискриминатора и типом объекта.
@NamedNativeQueries — аннотация используется для объединения нескольких аннотаций @NamedNativeQuery.
@NamedNativeQuery — аннотация расширяет JPA @NamedNativeQuery с особенностями Hibernate.
@NamedQueries — аннотация используется для группирования нескольких аннотаций @NamedQuery.
@NamedQuery — аннотация расширяет JPA @NamedQuery с особенностями Hibernate.
@Nationalized — аннотация используется, чтобы указать, что текущий аннотированный атрибут является символьным типом (например, String, Character, Clob), который хранится в национализированном типе столбцов (NVARCHAR, NCHAR, NCLOB).
@NaturalId — аннотация используется, чтобы указать, что текущий аннотированный атрибут является частью естественного идентификатора объекта.
@NaturalIdCache — аннотация используется для указания того, что значения естественного id, связанные с аннотированным объектом, должны храниться в кэше второго уровня.
@NotFound — аннотация используется для указания стратегии NotFoundAction для того, когда элемент не найден в данной ассоциации. NotFoundAction определяет с двумя возможностями: EXCEPTION - исключение генерируется, когда элемент не найден (по умолчанию и рекомендуется). IGNORE - игнорировать элемент, если он не найден в базе данных.
@OnDelete — аннотация используется для указания стратегии удаления, используемой текущей аннотированной коллекцией, массивом или присоединенными подклассами. Эта аннотация используется инструментом генерации автоматизированной схемы для генерирования соответствующей директивы каскада FOREIGN KEY DDL. Две возможные стратегии определяются перечислением OnDeleteAction: CASCADE - использовать каскадные возможности базы данных FOREIGN KEY. NO_ACTION - не предпринимать никаких действий.
@OptimisticLock — аннотация используется, чтобы указать, будет ли текущий аннотированный атрибут запускать приращение версии объекта при изменении.
@OptimisticLocking — аннотация используется для указания текущей аннотированной оптимизированной стратегии блокировки сущности. Четыре возможные стратегии определяются перечислением OptimisticLockType: NONE - неявный оптимистический механизм блокировки отключен. VERSION - Неявный оптимистический механизм блокировки использует выделенный столбец версии. ALL - Неявный оптимистический механизм блокировки использует все атрибуты как часть расширенного предложения WHERE для операторов UPDATE и DELETE SQL. DIRTY - Неявный оптимистический механизм блокировки использует грязные атрибуты (атрибуты, которые были изменены) как часть расширенного предложения WHERE для операторов UPDATE и DELETE SQL.
@OrderBy — аннотация используется для указания директивы упорядочения SQL для сортировки текущей аннотированной коллекции. Он отличается от аннотации JPA @OrderBy, поскольку аннотация JPA ожидает фрагмент упорядочивания JPQL, а не директиву SQL.
@ParamDef — аннотация используется в сочетании с @FilterDef, так что фильтр Hibernate можно настроить с помощью значений параметров, предоставляемых во время выполнения.
@Parameter — аннотация является общим параметром (в основном комбинация ключ / значение), используемым для параметризации других аннотаций, таких как @CollectionType, @GenericGenerator и @Type, @TypeDef.
@Parent — аннотация используется, чтобы указать, что текущий аннотированный вложенный атрибут ссылается на владеющий объект.
@Persister — аннотация используется для указания настраиваемого объекта или коллекции persister. Для сущностей пользовательский персист должен реализовать интерфейс EntityPersister. Для коллекций пользовательский пульт должен реализовывать интерфейс CollectionPersister.
@Polymorphism — аннотация используется для определения PolymorphismType Hibernate применяется к иерархиям сущностей. Возможны две опции PolymorphismType: EXPLICIT - текущая аннотированная сущность извлекается только при явном запросе. IMPLICIT - текущая аннотированная сущность извлекается, если какой-либо ее сущностный объект извлекается. Это опция по умолчанию.
@Proxy — аннотация используется для указания настраиваемой реализации прокси для текущего аннотированного объекта.
@RowId — аннотация используется для указания столбца базы данных, который используется как псевдоколонка ROWID. Например, Oracle определяет псевдоколонку ROWID, которая предоставляет адрес каждой строки таблицы. Согласно документации Oracle, ROWID - это самый быстрый способ доступа к одной строке из таблицы.
@SelectBeforeUpdate — аннотация используется, чтобы указать, что текущее аннотированное состояние сущности будет выбрано из базы данных при определении, выполнять ли обновление, когда отсоединенный объект присоединен.
@SortComparator — аннотация используется для указания компаратора для сортировки Set / Map в памяти.
@SortNatural — аннотация используется, чтобы указать, что Set / Map должен быть отсортирован с использованием естественной сортировки.
@Source — аннотация используется в сочетании с атрибутом объекта timestamp @Version, указывающим SourceType значения метки времени. SourceType предлагает два варианта: DB - получить временную метку из базы данных. VM - получить временную метку из текущей JVM.
@SQLDelete — аннотация используется для указания пользовательского оператора SQL DELETE для текущего аннотированного объекта или коллекции.
@SQLDeleteAll — аннотация используется для указания пользовательского оператора SQL DELETE при удалении всех элементов текущей аннотированной коллекции.
@SqlFragmentAlias — аннотация используется для указания псевдонима для Hibernate @Filter. Псевдоним (например, myAlias) можно затем использовать в предложении условия @Filter, используя алиас {alias} (например, {myAlias}).
@SQLInsert — аннотация используется для указания пользовательского оператора SQL INSERT для текущего аннотированного объекта или коллекции.
@SQLUpdate — аннотация используется для указания пользовательской инструкции SQL UPDATE для текущего аннотированного объекта или коллекции.
@Subselect — аннотация используется для указания неизменяемого и доступного только для чтения объекта с использованием пользовательского оператора SQL SELECT.
@Synchronize — аннотация обычно используется совместно с аннотацией @Subselect для указания списка таблиц базы данных, используемых в SQL-запросе @Subselect. С помощью этой информации Hibernate должным образом инициирует флеш-сущность всякий раз, когда должен выполняться запрос, предназначенный для объекта @Subselect, в то время как контекст Persistence запланировал некоторые действия insert / update / delete для таблиц базы данных, используемых в SQL-запросе @Subselect. Поэтому аннотация @Synchronize предотвращает возврат производной сущности устаревших данных при выполнении запросов сущностей против @ Subselect.
@Table — аннотация используется для указания дополнительной информации в аннотациях JPA @Table, например, пользовательские инструкции INSERT, UPDATE или DELETE или определенный FetchMode.
@Tables — аннотация используется для объединения нескольких аннотаций @Table.
@Target — аннотация используется для указания явной целевой реализации, когда текущая аннотированная ассоциация использует тип интерфейса.
@Tuplizer — аннотация используется для указания настраиваемого tuplizer для текущего аннотированного объекта или встраиваемого объекта. Для сущностей тапилизатор должен реализовывать интерфейс EntityTuplizer. Для встраиваемых устройств тапилизатор должен реализовывать интерфейс ComponentTuplizer.
@Tuplizers — аннотация используется для группирования нескольких аннотаций @Tuplizer.
@Type — аннотация используется для указания Hibernate @Type, используемого текущим аннотированным основным атрибутом.
@TypeDef — аннотация используется для определения @Type, которое впоследствии может быть повторно использовано для нескольких базовых сопоставлений атрибутов.
@TypeDefs — аннотация используется для группировки нескольких аннотаций @TypeDef.
@UpdateTimestamp — аннотация используется, чтобы указать, что текущий атрибут аннотированной отметки времени должен обновляться с текущей меткой времени JVM всякий раз, когда модифицируется сущность. Тип данных: Java.util.Date, Java.util.Calendar, Java.sql.Date, Java.sql.Time, Java.sql.Timestamp.
@Where — аннотация используется для указания настраиваемого предложения SQL WHERE, используемого при выборке объекта или коллекции.
@WhereJoinTable — аннотация используется для указания пользовательского предложения SQL WHERE, при извлечении данных JOIN соединенных таблиц базы данных.
Кеш 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. Что нужно сделать:
- добавить мавен-зависимость кэш-провайдера нужной версии:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.2.Final</version>
</dependency>
- включить кэш второго уровня и определить конкретного провайдера:
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
- установить у нужных сущностей JPA-аннотацию @Cacheable, обозначающую, что сущность нужно кэшировать, и Hibernate-аннотацию @Cache, настраивающую детали кэширования, у которой в качестве параметра указать стратегию параллельного доступа (о которой говорится далее), например так:
@Entity
@Table(name = "shared_doc")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SharedDoc{
private Set<User> users;
}
-
не обязательно устанавливать у сущностей JPA-аннотацию @Cacheable, если работаем с Hibernate напрямую, не через JPA.
-
чтобы кэш не “съел” всю доступную память, можно, например, ограничивать количество каждого типа сущностей, хранимых в кэше:
<ehcache>
<cache name="com.baeldung.persistence.model.Foo" maxElementsInMemory="1000"/>
</ehcache>
Стратегия параллельного доступа к объектам
Проблема заключается в том, что кэш второго уровня доступен из нескольких сессий сразу и несколько потоков программы могут одновременно в разных транзакциях работать с одним и тем же объектом. Следовательно надо как-то обеспечивать их одинаковым представлением этого объекта. В Hibernate существует четыре стратегии одновременного доступа к объектам в кэше:
-
READ_ONLY: Используется только для сущностей, которые никогда не изменяются (будет выброшено исключение, если попытаться обновить такую сущность). Очень просто и производительно. Подходит для некоторых статических данных, которые не меняются.
-
NONSTRICT_READ_WRITE: Кэш обновляется после совершения транзакции, которая изменила данные в БД и закоммитила их. Таким образом, строгая согласованность не гарантируется, и существует небольшое временное окно между обновлением данных в БД и обновлением тех же данных в кэше, во время которого параллельная транзакция может получить из кэша устаревшие данные.
-
READ_WRITE: Эта стратегия гарантирует строгую согласованность, которую она достигает, используя «мягкие» блокировки: когда обновляется кэшированная сущность, на нее накладывается мягкая блокировка, которая снимается после коммита транзакции. Все параллельные транзакции, которые пытаются получить доступ к записям в кэше с наложенной мягкой блокировкой, не смогут их прочитать или записать и отправят запрос в БД. Ehcache использует эту стратегию по умолчанию.
-
TRANSACTIONAL: полноценное разделение транзакций. Каждая сессия и каждая транзакция видят объекты, как если бы только они с ним работали последовательно одна транзакция за другой. Плата за это — блокировки и потеря производительности.
Представление объектов в кэше
Еще одна важная деталь про кэш второго уровня о которой стоило бы упомянуть — Hibernate не хранит сами объекты Ваших классов. Он хранит информацию в виде массивов строк, чисел и т.д. Что очень разумно, учитывая сколько лишней памяти занимает каждый объект. Идентификатор объекта выступает указателем на эту информацию. Концептуально это нечто вроде Map, в которой id объекта — ключ, а массивы данных — значения полей. Приблизительно это можно представить себе так:
1 -> { "Pupkin", 1, null , {1,2,5} }
Помимо вышесказанного, следует помнить — зависимости Вашего класса по умолчанию также не кэшируются. Например, рассмотрим класс SharedDoc, который кэшируется:
@Entity
@Table(name = "shared_doc")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SharedDoc{
private Set<User> users;
}
В примере выше при выборке сущности SharedDoc из кэша, коллекция users будет доставаться из БД, а не из кэша второго уровня. Если мы хотим также кэшировать и зависимости, то над полями тоже нужно разместить аннотации @Cacheable и @Cache:
@Entity
@Table(name = "shared_doc")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SharedDoc{
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<User> 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, установив для параметра
<property name="hibernate.cache.use_query_cache" value="true"/>
и определив
hibernate.cache.region.factory_class
Кроме того, вам также необходимо активировать кэширование для конкретного запроса, для которого вы хотите кэшировать результаты, вызывая
setCacheable(true)
или через подсказку в запросе setHint("org.hibernate.cacheable", true):
entityManager.createQuery("select f from Foo f")
.setHint("org.hibernate.cacheable", true)
.getResultList();
У кэша запросов есть и своя цена — Hibernate будет вынужден отслеживать сущности закешированные с определённым запросом и выкидывать запрос из кэша, если кто-то поменяет значение сущности. То есть для кэша запросов стратегия параллельного доступа всегда read-only.
@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 использует прокси объект для поддержки отложенной загрузки. Обычно при загрузке данных из таблицы Hibernate не загружает все отображенные (замаппинные) объекты. Как только вы ссылаетесь на дочерний объект или ищите объект с помощью геттера, если связанная сущность не находиться в кэше сессии, то прокси код перейдет к базе данных для загрузки связанной сущности. Для этого используется javassist, чтобы эффективно и динамически создавать реализации подклассов ваших entity объектов.
- LAZY - не грузит связанные сущности, только при обращении
- EAGER - грузит сразу все связанные сущности
Например, есть Owner(один) и Book(много).
@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<Book> 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(). Сделать такое:
@Override
public Owner read(Long id) {
Owner owner = ownerRepository.findOne(id);
owner.getBooks().iterator();
return owner;
}
Или Hibernate.initialize(owner.getBooks()); Это костыль, но он заставит хибернейт инициировать коллекцию.
Стратегии кеширования определяют поведения кеша в определенных ситуациях. Выделяют четыре группы:
- Read-only — объекты кэшируются только для чтения и изменение удаляет их из кэша.
- Read-write — полноценный доступ к одной конкретной записи и разделение её состояния между транзакциями. Однако суммарное состояние нескольких объектов в разных транзакциях может отличаться.
- Nonstrict-read-write — аналогичен read-write, но изменения объектов могут запаздывать и транзакции могут видеть старые версии объектов. Рекомендуется использовать в случаях, когда одновременное обновление объектов маловероятно и не может привести к проблемам.
- Transactional — полноценное разделение транзакций. Каждая сессия и каждая транзакция видят объекты, как если бы только они с ним работали последовательно одна транзакция за другой. Плата за это — блокировки и потеря производительности.
Стратегии наследования нужны для того, чтобы дать понять провайдеру(Hibernate) как ему отображать в БД сущности-наследники. Для этого нам нужно декорировать родительский класс аннотацией @Inheritance и указать один из типов отображения: SINGLE_TABLE, TABLE_PER_CLASS, JOINED.
- SINGLE_TABLE. Одна таблица на всю иерархию классов.
- TABLE_PER_CLASS. Таблица для каждого конкретного класса сущностей.
- JOINED. Стратегия «соединения», при которой поля или свойства, специфичные для подклассов, отображаются в таблицах этих подклассов, а поля или свойства родительского класса отображаются в таблице родительского класса.
- одна таблица на всю иерархию наследования (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")
Если мы хотим поменять имя колонки, то мы должны указать её новое имя в параметре аннотации у класса-родителя: @DiscriminatorColumn(name=EMP_TYPE).
@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;
}
Эта стратегия обеспечивает хорошую поддержку полиморфных отношений между сущностями и запросами, которые охватывают всю иерархию классов сущностей
-- 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 для тех колонок таблицы, которые характерны только для классов-наследников.
- объединяющая стратегия (joined subclass strategy) — в этой стратегии каждый класс enity сохраняет данные в свою таблицу, но только уникальные колонки (не унаследованные от классов-предков) и первичный ключ, а все унаследованные колонки записываются в таблицы класса-предка, дополнительно устанавливается связь (relationships) между этими таблицами, например в случае классов Animals (см.выше), будут три таблицы animals, cats, dogs, причем в cats будет записана только ключ и скорость лазанья, в dogs — ключ и умеет ли пес приносить палку, а в animals все остальные данные cats и dogs c ссылкой на соответствующие таблицы. Минусом тут являются потери производительности от объединения таблиц (join) для любых операций.
Столбец первичного ключа в таблице подкласса служит внешним ключом первичного ключа таблицы суперкласса. Также в таблице родительского класса добавляется столбец DiscriminatorColumn с DiscriminatorValue для определения типа наследника.
@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]
Эта стратегия обеспечивает хорошую поддержку полиморфных отношений, но требует выполнения одной или нескольких операций соединения таблиц при создании экземпляров подклассов сущностей. В глубоких иерархиях классов это может привести к недопустимому снижению производительности. Точно так же запросы, которые покрывают всю иерархию классов, требуют операций соединения между таблицами подклассов, что приводит к снижению производительности:
-- Loading entities --
List<Employee> 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'}
- одна таблица для каждого класса (table per concrete class strategy) — каждый отдельный класс-наследник имеет свою таблицу, т.е. для cats и dogs (см.выше) все данные будут записываться просто в таблицы cats и dogs как если бы они вообще не имели общего суперкласса. Минусом является плохая поддержка полиморфизма (polymorphic relationships) и то что для выборки всех классов иерархии потребуются большое количество отдельных sql запросов или использование UNION запроса.
@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<Employee> 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, поскольку может быть несколько объектов подкласса, имеющих один и тот же идентификатор, и запрос базового класса приведет к получению объектов с одним и тем же идентификатором (даже если они принадлежат разным типам).
Join fetching: hibernate получает ассоциированные объекты и коллекции одним SELECT используя OUTER JOIN
Select fetching: использует уточняющий SELECT чтобы получить ассоциированные объекты и коллекции. Если вы не установите lazy fetching определив lazy="false", уточняющий SELECT будет выполнен только когда вы запрашиваете доступ к ассоциированным объектам
Subselect fetching: поведение такое же, как у предыдущего типа, за тем исключением, что будут загружены ассоциации для все других коллекций, "родительским" для которых является сущность, которую вы загрузили первым SELECT’ом.
Batch fetching: оптимизированная стратегия вида select fetching. Получает группу сущностей или коллекций в одном SELECT’е
Basic — указывает на простейший тип маппинга данных на колонку таблицы базы данных. Также в параметрах аннотации можно указать fetch стратегию доступа к полю и является ли это поле обязательным или нет.
В широком смысле Hibernate разделяет типы на две группы:
- Типы значений (Value types).
- Типы сущностей (Entity types).
Типы сущностей Сущности из-за своего уникального идентификатора существуют независимо от других объектов, тогда как типы значений нет. Экземпляры сущностей соответствуют строкам в таблице базы данных и различаются между собой благодаря уникальным идентификаторам. Например, две сущности могут иметь абсолютно одинаковые значения полей, но имея разные идентификаторы (первичные ключи) они будут считаться разными, в отличие от POJO, которые при наличии абсолютно одинаковых значений полей будут считаться равными (equals вернет true). Из-за требования к наличию уникального идентификатора, сущности существуют независимо и определяют свой собственный жизненный цикл.
Типы значений Это данные, которые не определяют свой собственный жизненный цикл. По сути, они принадлежат сущности (entity), которая определяет их жизненный цикл. С другой стороны, всё состояние объекта полностью состоит из типов значений. В свою очередь, типы значений подразделяются на три подкатегории:
-
Базовые типы (Basic types).
-
Встраиваемые типы (Embeddable types).
-
Типы коллекций (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 атрибута:
-
optional - boolean (по умолчанию true) - определяет, может ли значение поля или свойства быть null. Игнорируется для примитивных типов. Но если тип поля не примитивного типа, то при попытке сохранения сущности будет выброшено исключение.
-
fetch - FetchType (по умолчанию EAGER) - определяет, должен ли этот атрибут извлекаться незамедлительно (EAGER) или лениво (LAZY). Однако, это необязательное требование JPA, и провайдерам разрешено незамедлительно загружать данные, даже для которых установлена ленивая загрузка.
Без аннотации @Basic при получении сущности из БД по умолчанию её поля базового типа загружаются принудительно (EAGER) и значения этих полей могут быть null
Аннотация @Column сопоставляет поле класса столбцу таблицы, а её атрибуты определяют поведение в этом столбце, используется для генерации схемы базы данных
@Basic vs @Column:
- Атрибуты @Basic применяются к сущностям JPA, тогда как атрибуты @Column применяются к столбцам базы данных.
- @Basic имеет атрибут optional, который говорит о том, может ли поле объекта быть null или нет; с другой стороны атрибут nullable аннотации @Column указывает, может ли соответствующий столбец в таблице быть null.
- Мы можем использовать @Basic, чтобы указать, что поле должно быть загружено лениво.
- Аннотация @Column позволяет нам указать имя столбца в таблице и ряд других свойств: a. insertable/updatable - можно ли добавлять/изменять данные в колонке, по умолчанию true; b. length - длина, для строковых типов данных, по умолчанию 255
Она определяет тип доступа (access type) для класса entity, суперкласса, embeddable или отдельных атрибутов, то есть как JPA будет обращаться к атрибутам entity, как к полям класса (FIELD) или как к свойствам класса (PROPERTY), имеющие гетеры (getter) и сетеры (setter).
Hibernate или другой провайдер должен каким-то образом получать доступ к полям сущности. Например, при сохранении сущности в базу данных Hibernate должен получить доступ к состоянию сущности, то есть прочитать значения полей сущности, чтобы записать их в соответствующие ячейки таблицы. Аналогично при получении данных из БД и формировании из них объекта сущности, Hibernate должен создать этот самый объект сущности (для этого ему и нужен public или protected конструктор без параметров), а затем записать в поля этого объекта значения, полученные из ячеек БД, тем самым сформировав состояние сущности.
Для чтения и записи этих полей Hibernate использует два подхода:
- Field access (доступ по полям). При таком способе аннотации маппинга (Id, Column, OneToMany, … ) размещаются над полями, и Hibernate напрямую работает с полями сущности, читая и записывая их.
- 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 для избежания дублирования маппинга.
@Embeddable Аннотация JPA, размещается над классом для указания того, что класс является встраиваемым в другие классы и будет внедрен другими сущностями, то есть поля этого встраиваемого класса будут добавляться к полям других сущностей и будут представлять столбцы в таблице этой сущности. Так, во встраиваемый класс мы можем выделить общие поля для разных сущностей не создавая для него таблицу. Встраиваемый класс сам не является сущностью.
@Embeddable
public class ContactPerson {
private String firstName;
private String lastName;
private String phone;
// standard getters, setters
}
@Embedded Аннотация JPA, используется для размещения над полем в классе-сущности для указания того, что мы внедряем встраиваемый класс.
@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.
@Entity
public class Office {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "addressId")
private Address address;
}
- @OneToMany/@ManyToOne: в данном случае мы можем использовать параметр mappedBy для того, чтобы столбец с внешними ключами находился на владеющей стороне ManyToOne - то есть в таблице Email:
@Entity
public class Employee {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "employee")
private List<Email> emails;
}
@Entity
public class Email {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "employee_id")
private Employee employee;
}
В приведенном выше примере таблица Email (владелец связи) имеет столбец employee_id, в котором хранится значение идентификатора и внешний ключ к таблице Employee.
- Если бы мы не указали mappedBy, то была бы создана сводная (третья)таблица с первичными ключами из двух основных таблиц.
@JoinColumns
Аннотация @JoinColumns используется для группировки нескольких аннотаций @JoinColumn, которые используются при установлении связей между сущностями или коллекциями, у которых составной первичный ключ и требуется несколько колонок для указания внешнего ключа.
В каждой аннотации @JoinColumn должны быть указаны элементы name и referencedColumnName:
@ManyToOne
@JoinColumns({
@JoinColumn(name="ADDR_ID", referencedColumnName="ID"),
@JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP")
})
public Address getAddress() { return address; }
@JoinTable
Аннотация @JoinTable используется для указания связывающей (сводной, третьей) таблицы между двумя другими таблицами.
@OrderBy
Аннотация @OrderBy указывает порядок, в соответствии с которым должны располагаться элементы коллекций сущностей, базовых или встраиваемых типов при их извлечении из БД. Эта аннотация может использоваться с аннотациями @ElementCollection, @OneToMany, @ManyToMany.
При использовании с коллекциями базовых типов, которые имеют аннотацию @ElementCollection, элементы этой коллекции будут отсортированы в натуральном порядке, по значению базовых типов:
@ElementCollection
@OrderBy
private List<String> phoneNumbers;
В данном примере коллекция строк phoneNumbers, будет отсортирована в натуральном порядке, по значениям базового типа String.
Если это коллекция встраиваемых типов (@Embeddable), то используя точку(".") мы можем сослаться на атрибут внутри встроенного атрибута. Например, следующий код отсортируют адреса по названиям стран в обратном порядке:
@Embeddable
public class Address {
...
@Embedded
private City city
....
}
@ElementCollection
@OrderBy("city.country DESC")
private List<Address> addresses;
Если это коллекция сущностей, то у аннотации @OrderBy можно указать имя поля сущности, по которому сортировать эти самые сущности:
@Entity
public class Task {
....
@OneToOne
private Employee supervisor;
...
}
@ManyToMany
@OrderBy("supervisor")
private List<Task> tasks
Если мы не укажем у @OrderBy параметр, то сущности будут упорядочены по первичному ключу.
В случае с сущностями доступ к полю по точке (".") не работает. Попытка использовать вложенное свойство, например @OrderBy ("supervisor.name") повлечет Runtime Exceprtion.
@OrderColumn
Аннотация @OrderColumn создает столбец в таблице, который используется для поддержания постоянного порядка в списке, но этот столбец не считается частью состояния сущности или встраиваемого класса.
Hibernate отвечает за поддержание порядка как в базе данных при помощи столбца, так и при получении сущностей и элементов из БД. Hibernate отвечает за обновление порядка при записи в базу данных, чтобы отразить любое добавление, удаление или иное изменение порядка, влияющее на список в таблице.
Аннотация @OrderColumn может использоваться с аннотациями @ElementCollection, @OneToMany, @ManyToMany - указывается на стороне отношения, ссылающегося на коллекцию, которая должна быть упорядочена. В примере ниже Hibernate добавил в таблицу Employee_PhoneNumbers третий столбец PHONENUMBERS_ORDER, который и является результатом работы @OrderColumn:
'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 методов (и аннотаций с теми же именами):
- PrePersist
- PostPersist
- PreRemove
- PostRemove
- PreUpdate
- PostUpdate
- PostLoad
У JPA есть шесть видов блокировок, перечислим их в порядке увеличения надежности (от самого ненадежного и быстрого, до самого надежного и медленного):
- NONE — без блокировки
- OPTIMISTIC (или синоним READ, оставшийся от JPA 1) — оптимистическая блокировка,
- OPTIMISTIC_FORCE_INCREMENT (или синоним WRITE, оставшийся от JPA 1) — оптимистическая блокировка с принудительным увеличением поля версионности,
- PESSIMISTIC_READ — пессимистичная блокировка на чтение,
- PESSIMISTIC_WRITE — пессимистичная блокировка на запись (и чтение),
- PESSIMISTIC_FORCE_INCREMENT — пессимистичная блокировка на запись (и чтение) с принудительным увеличением поля версионности.
Оптимистичное блокирование предполагает, что параллельно выполняющиеся транзакции редко обращаются к одним и тем же данным и позволяет им спокойно и свободно выполнять любые чтения и обновления данных. Но при окончании транзакции производится проверка, изменились ли данные в ходе выполнения данной транзакции и, если да, транзакция обрывается и выбрасывается исключение. Оптимистичное блокирование в JPA реализовано путём внедрения в сущность специального поля версии:
@Entity
public class Company extends AbstractIdentifiableObject {
@Version
private long version;
@Getter
@Setter
private String name;
@Getter
@Setter
@ManyToMany(mappedBy = "workingPlaces")
private Collection<Person> workers;
}
Поле, аннотирование @Version, может быть целочисленным или временнЫм. При завершении транзакции, если сущность была оптимистично заблокирована, будет проверено, не изменилось ли значение @Version кем-либо ещё, после того как данные были прочитаны, и, если изменилось, будет выкинуто OptimisticLockException. Использование этого поля позволяет отказаться от блокировок на уровне базы данных и сделать всё на уровне JPA, улучшая уровень конкурентности.
JPA поддерживает два типа оптимистичной блокировки:
- LockModeType.OPTIMISTIC — блокировка на чтение, которая работает, как описано выше: если при завершении транзакции кто-то извне изменит поле @Version, то транзакция автоматически будет откачена и будет выброшено OptimisticLockException.
- LockModeType.OPTIMISTIC_FORCE_INCREMENT — блокировка на запись. Ведёт себя как и блокировка на чтение, но при этом увеличивает значение поля @Version.
Обе блокировки ставятся путём вызова метода lock() у EntityManager, в который передаётся сущность, требующая блокировки и уровень блокировки:
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():
em.lock(company1, LockModeType.PESSIMISTIC_READ);
em.lock(company2, LockModeType.PESSIMISTIC_WRITE);
em.lock(company3, LockModeType.PESSIMISTIC_FORCE_INCREMENT);
Снимаются они тоже автоматически, по завершению транзакции.
Следующие публичные методы EntityManager-а могут использоваться для наложения блокировок:
void lock(Object entity, LockModeType lockMode)
void lock(Object entity, LockModeType lockMode, Map<String, Object>
properties)
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType
lockMode)
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType
lockMode, Map<String, Object> properties)
void refresh(Object entity, LockModeType lockMode)
void refresh(Object entity, LockModeType lockMode, Map<String, Object>
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:
@NamedEntityGraphs({
@NamedEntityGraph(
name = 'customer.products',
attributeNodes = {
@NamedAttributeNode("products")
}
)
})
@Entity
@Table(name = "customer")
- javax.persistence.fetchgraph — все атрибуты перечисленные в EntityGraph меняют fetchType на EAGER, все остальные на LAZY
- javax.persistence.loadgraph — все атрибуты перечисленные в EntityGraph меняют fetchType на EAGER, все остальные сохраняют свой fetchType (то есть если у атрибута, не указанного в EntityGraph, fetchType был EAGER, то он и останется EAGER)С помощью NamedSubgraph можно также изменить fetchType вложенных объектов Entity.
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<Book> fetchAuthors = em.createEntityGraph(Book.class); //чтобы использовать EntityGraph, нужно достать его из EntityManager
fetchAuthors.addSubgraph(Book_.authors);
List<Book> 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 для получения связанной сущности
Что означает полиморфизм (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).
В чем разница в требованиях к Entity в Hibernate, от требований к Entity, указанных в спецификации JPA
-
Конструктор без аргументов в Hibernate не обязан быть public или protected, рекомендуется чтобы он был хотя бы package видимости, однако это только рекомендация, если настройки безопасности Java позволяют доступ к приватным полям, то он может быть приватным.
-
JPA категорически требует не использовать final классы, Hibernate лишь рекомендует не использовать такие классы чтобы он мог создавать прокси для ленивой загрузки, однако позволяет либо выключить прокси Proxy(lazy=false), либо использовать в качестве прокси интерфейс, содержащий все методы маппинга для данного класса (аннотацией Proxy(proxyClass=интерфейс.class) )
При наличии зависимостей (связей) между сущностями необходимо определить влияние различных операций одной сущности на связанные. Это можно реализовать с помощью аннотации каскадных связей @Cascade.
Помните, что имеются некоторые различия между enum CascadeType в Hibernate и в JPA. Поэтому обращайте внимание на импортируемый пакет при использовании аннотации и константы типа. Наиболее часто используемые CascadeType перечисления описаны ниже :
- None : без каскадирования, т.е. никакая операция для родителя не будет иметь эффекта для ребенка;
- ALL : все операции родителя будут отражены на ребенке (save, delete, update, evict, lock, replicate, merge, persist);
- SAVE_UPDATE : операции save и update, доступно только для hibernate;
- DELETE : в Hibernate передается действие native DELETE;
- DETATCH, MERGE, PERSIST, REFRESH, REMOVE – для простых операций;
- LOCK : передает в Hibernate native LOCK действие;
- REPLICATE : передает в Hibernate native REPLICATE действие.
Удаление сирот в отношениях (Orphan Removal)
Представим, что у нас есть класс Customer, у которого есть коллекция Order:
@Entity
public class Customer {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// other mappings, getters and setters
}
Пусть у нас есть один объект Customer - родитель, в коллекции которого есть 4 объекта Order - дети, и мы установили атрибут orphanRemoval = true над этой коллекцией. В нашей базе данных в таблице Customer будет одна строка, а в таблице Order будет четыре строки. Также в таблице Order будет колонка с внешними ключами на таблицу Customer. В каждой из четырех ячеек этой колонки будут ссылки на один и тот же первичный ключ объекта Customer.
Например, мы удалим из коллекции orders один объект Order - любой из четырех, в результате чего у объекта Customer останется три объекта Order:
Customer customer = entityManager.find(Customer.class, 1L);
Order order = customer.getOrders().get(0);
customer.getOrders().remove(order);
flushAndClear();
После запуска метода flushAndClear() - обновления объекта Customer отправятся в базу данных, и произойдет следующее:
- Hibernate заметит, что у объекта Customer уже не 4, а 3 связанных дочерних объекта Order;
- в связи с этим Hibernate найдёт в таблице Order строку с удаленным объектом из коллекции Order;
- очистит в этой строке ячейку с внешним ключом на Customer;
- после чего удалит саму эту строку, как осиротевшую (более не ссылающуюся на родителя).
Если не будет атрибута orphanRemoval = true, то пункт 4 не выполнится, и в таблице Order останется сущность Order, не связанная ни с одной сущностью Customer, то есть её ячейка с внешним ключом будет пустой. Такая сущность будет считаться осиротевшей.
Существуют следующие четыре типа связей между сущностями:
- OneToOne - когда один экземпляр Entity может быть связан не больше чем с одним экземпляром другого Entity.
- OneToMany - когда один экземпляр Entity может быть связан с несколькими экземплярами других Entity.
- ManyToOne - обратная связь для OneToMany. Несколько экземпляров Entity могут быть связаны с одним экземпляром другого Entity.
- ManyToMany - экземпляры Entity могут быть связаны с несколькими экземплярами друг друга.
mappedBy Владеемая сторона в двунаправленных отношениях должна ссылаться на владеющую сторону используя элемент mappedBy аннотаций @OneToOne, @OneToMany, или @ManyToMany. Элемент mappedBy определяет поле в объекте, который является владельцем отношения. Если применить атрибут mappedBy на одной стороне связи, то Hibernate не станет создавать сводную таблицу:
@Entity
@Table(name="CART")
public class Cart {
//...
@OneToMany(mappedBy="cart")
private Set<Items> 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 часто перемещаются между отношениями. Направление отношений определяет, может ли запрос перемещаться от одной сущности к другой. Например в двунаправленных отношениях запрос может перемещаться как от первой сущности ко второй, так и обратно. В однонаправленных отношениях запрос может перемещаться только в одну сторону - от владеющей сущности к владеемой
В OneToOne без mappedBy хибер явно указывает двустороннюю связь. У Юзера есть колонка с Машинами, у каждой машины есть колонка с Юзером. В итоге если у одного юзера 3 машины, но будут сохраненны 3 строчки разных машин у которых будет одинаковый Юзер.
При использовании mappedBy, у Машин не будет столбца Юзер, т.к. у каджого Юзера и так указываются Id Машин. Если нужно обратиться к конкретной машине, обращение к ней идет через юзера.
При других видах связи, без использования mappedBy хибер создаёт смежную таблицу, в которой связывает значениях таблиц. Что ухудшает производительность.
При использовании mappedBy смежных таблиц нет, т.к. хибер уже точно значет кто на что ссылается.
В JPA описаны два типа fetch-стратегии:
- LAZY — данные поля сущности будут загружены только во время первого обращения к этому полю.
- EAGER — данные поля будут загружены немедленно вместе с сущностью.
FetchType.EAGER: Hibernate должен сразу загрузить соответствующее аннотированное поле или свойство. Это поведение по умолчанию для полей, аннотированных @Basic, @ManyToOne и @OneToOne (все что быстро).
FetchType.LAZY: Hibernate может загружать данные не сразу, а при первом обращении к ним, но так как это необязательное требование, то Hibernate имеет право изменить это поведение и загружать их сразу. Это поведение по умолчанию для полей, аннотированных @OneToMany, @ManyToMany и @ElementCollection (все что медленно) .
Раньше у Hibernate все поля были LAZY, но в последних версиях - всё как в JPA к оглавлению
По порядковым номерам EnumType.ORDINAL Если мы сохраняем в БД сущность, у которой есть поле-перечисление (Enum), то в таблице этой сущности создаётся колонка для значений этого перечисления и по умолчанию в ячейки сохраняется порядковый номер этого перечисления (ordinal).
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. содержащем числовое значение приоритета:
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, нам нужно добавить два атрибута и реализовать методы обратного вызова:
@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.
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<Category, String> {
@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. Таким образом, наше приложение будет независимым от текущего часового пояса системы.
Другой способ - настроить свойство hibernate.jdbc.time_zone в файле свойств Hibernate, который используется для создания фабрики сессий. Таким образом, мы можем указать часовой пояс один раз для всего приложения.
java.sql Hibernate позволяет отображать различные классы даты/времени из Java в таблицах баз данных. Стандарт SQL определяет три типа даты/времени:
- DATE - Представляет календарную дату путем хранения лет, месяцев и дней. Эквивалентом JDBC является java.sql.Date.
- TIME - Представляет время дня и хранит часы, минуты и секунды. Эквивалентом JDBC является java.sql.Time.
- TIMESTAMP - Хранит как DATE, так и TIME плюс наносекунды. Эквивалентом JDBC является java.sql.Timestamp. Поскольку эти типы соответствуют SQL, их сопоставление относительно простое. Мы можем использовать аннотацию @Basic или @Column:
@Entity
public class TemporalValues {
@Basic
private java.sql.Date sqlDate;
@Basic
private java.sql.Time sqlTime;
@Basic
private java.sql.Timestamp sqlTimestamp;
}
Затем мы могли бы установить соответствующие значения следующим образом:
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
java.util Точность представления времени составляет одну миллисекунду. Для большинства практических задач этого более чем достаточно, но иногда хочется иметь точность повыше.
Поскольку классы в данном API изменяемые (не immutable), использовать их в многопоточной среде нужно с осторожностью. В частности java.util.Date можно признать «эффективно» потоко-безопасным, если вы не вызываете у него устаревшие методы.
java.util.Date Тип java.util.Date содержит информацию о дате и времени с точностью до миллисекунд. Но так как классы из этого пакета не имели прямого соответствия типам данных SQL, приходилось использовать над полями java.util.Date аннотацию @Temporal, чтобы дать понять SQL, с каким конкретно типом данных она работает. Для этого у аннотации @Temporal нужно было указать параметр TemporalType, который принимал одно из трёх значений: DATE, TIME или TIMESTAMP, что позволяло указать базе данных с какими конкретными типами данных она работает.
@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:
@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:
- LocalDate соответствует DATE.
- LocalTime и OffsetTime соответствуют TIME.
- Instant, LocalDateTime, OffsetDateTime и ZonedDateTime соответствуют TIMESTAMP.
Это означает, что мы можем пометить эти поля только аннотацией @Basic (или @Column), например:
@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 с использованием соответствующего формата. Итак, вот как мы можем установить значения полей сущности:
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]"));
Экземпляр EntityManager связан с persistence context. Контекст сохранения-это кэш первого уровня, в котором все сущности извлекаются из базы данных или сохраняются в ней. Он находится между нашим приложением и постоянным хранилищем. Набор экземпляров сущности, в котором для любого персистентного идентификатора сущности существует уникальный экземпляр сущности. В persistence context управляются экземпляры сущностей и их жизненный цикл. API EntityManager используется для создания и удаления постоянных экземпляров сущностей, поиска сущностей по их первичному ключу и выполнения запросов к сущностям.
PersistenceContext доступны в двух типах: PersistenceContext в области транзакций и PersistenceContext расширенной области
- Транзакционный контекст персистентности. Контекст постоянства транзакции привязан к транзакции. Как только транзакция заканчивается, объекты, присутствующие в контексте постоянства, будут сброшены в постоянное хранилище. Когда мы выполняем какую-либо операцию внутри транзакции, EntityManager проверяет PersistenceContext. Если он существует, он будет использован. В противном случае это создаст PersistenceContext. Тип контекста персистентности по умолчанию - PersistenceContextType.TRANSACTION. Чтобы указать EntityManager использовать контекст постоянства транзакции, мы просто аннотируем его с помощью @PersistenceContext :
@PersistenceContext
private EntityManager entityManager;
- Расширенный персистентный контекст Расширенный контекст постоянства может охватывать несколько транзакций. Мы можем сохранить сущность без транзакции, но не можем сбросить ее без транзакции. Чтобы сказать EntityManager использовать контекст персистентности расширенной области, нам нужно применить атрибут type @PersistenceContext:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
https://www.baeldung.com/jpa-hibernate-persistence-context
EntityGraph (граф сущностей) - механизм динамического изменения fetchType для каждого запроса. Используя аннотации можно описать то, что будем выбирать из базы. Граф применяется на уровне SQL запроса, таким образом “лишние” данные не выбираются в Java приложение. Но есть одна небольшая проблема: нельзя сказать, какие атрибуты были выбраны, а какие — нет. Для проверки есть API, это делается при помощи класса PersistenceUtil.
@EntityGraph(value = "customer.products")
List<Customer> findAll(@Nullable Specification<Customer> specification)
Для сложных зависимостей в запросах можно использовать NamedEntityGraph, но его нужно явно объявить в самой Entity
Основная цель JPA Entity Graph - улучшить производительность в рантайме при загрузке базовых полей сущности и связанных сущностей и коллекций.
Вкратце, Hibernate загружает весь граф в одном SELECT-запросе, то есть все указанные связи от нужной нам сущности:
@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<Comment> comments = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private User user;
//...
}
В данном примере мы создали EntityGraph и при его помощи хотим, чтобы при загрузке сущности Post загружались поля subject, user, comments. Также мы захотели, чтобы у каждой сущности comments загружалось поле user, для чего мы использовали subgraphs.
EntityGraph можно определить и без помощи аннотаций, а используя entityManager из JPA API:
EntityGraph<Post> 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 можем тремя способами:
- Используя перегруженный метод find(), который принимает Map с настройкам EntityGraph:
EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph");
Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.fetchgraph", entityGraph);
Post post = entityManager.find(Post.class, id, properties);
- Используя JPQL и передав подсказку (hint):
EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph-with�comment-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();
- С помощью Criteria API:
EntityGraph entityGraph = entityManager.getEntityGraph("post-entity-graph-with�comment-users");
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Post> criteriaQuery = criteriaBuilder.createQuery(Post.class);
Root<Post> root = criteriaQuery.from(Post.class);
criteriaQuery.where(criteriaBuilder.equal(root.<Long>get("id"), id));
TypedQuery<Post> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setHint("javax.persistence.loadgraph", entityGraph);
Post post = typedQuery.getSingleResult();
Все они дают следующий результат:
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/
Есть User и которого есть коллекция машин Cars. Если мы хотим получить список из 10 юзеров, то в бд полетит 11 запросов. 1 запрос на получение самих юзеров и еще 10 запросов на получение списка их машин, отдельно для каждого User. Это приводит к серьёзным проблемам с производительностью.
JOIN FETCH
Самое правильное решение - использовать JOIN FETCH и jpql на выборку сущности. Данное решение не поддерживает работу с нативными запросами, но работает любым видом OneToMany/ManyToOne связи. Может генерировать дополнительные подзапросы, тогда возможно лучше entityGraph брать, чтобы вместо подзапросов join'ы генерировались
FetchMode.SUBSELECT
Это Аннотация Hibernate, в JPA её нет. Можно использовать только с коллекциями. Будет сделан один sql-запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям-коллекциям, то выполнится еще один запрос для получения связанных коллекций
EntityGraph
Не самое изящное решение для n+1 проблемы. Графы в основном нужны, когда требуется загрузить действительно большой детальный граф, т.е. когда нам нужно получить очень много связанной информации из базы, и такой большой запрос следует оптимизировать. Для n+1 не самое компактное решение, но тоже работает, однако такое решение не подойдет при использовании нативных запросов.
Batch fetching
Это Аннотация Hibernate, в JPA её нет. Указывается над классом сущности или над полем коллекции с ленивой загрузкой. Будет сделан один sql-запрос для получения корневых сущностей и, если в контексте персистентности будет обращение к ленивым полям-коллекциям, то выполнится еще один запрос для получения связанных коллекций. Изменим пример:
@OneToMany(mappedBy = "customer")
@Fetch(value = FetchMode.SELECT)
@BatchSize(size=5)
private Set<Order> 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 одновременно:
@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 emp.department dep
возвращается только сам запрощенный объект. При запросе JOIN FETCH emp.department dep
возвращается объект и связанные с ним сущности, в одном запросе.
Не стоит использовать для простых запросов, посколько нужно писать много кода. Хорош при использовании со SpringData для сложных запросов.
Например нужно создать оч сложный, кастомный фильтр. Создаем класс, имплеметирующий интерфейс Specification<?>, в нем метод toPredicate, который берет наш фильтр, превращает его в предикейт и возвращающет Predicate. А затем посредством SpringData, в дефолтные методы JPARepository например, вставляем эту спецификацию к оглавлению
OrderBy упорядычевает результат запроса (от меньшего к большему и т.д.).
Указывает столбец, который используется для поддержания постоянного порядка списка. @OrderColumn указывается в отношении OneToMany или ManyToMany или в коллекции элементов. @OrderColumn указывается на стороне отношения, ссылающейся на коллекцию, которая должна быть упорядочена. OrderColumn сохраняет порядок в List, используя выделенный столбец данных. Если в листе Юзеров = 1, 2, 3, 4 будет удален Юзер 3, то результат запроса будет 1, 2, Null, 4. Т.е. порядок не нарушится.
@OrderBy в запросе отсортирует, а в кэше вернет неотсортированный порядок. @OrderedColumn сортирует данные с учетом данных в колонке, и в кеше и в запросе. Указанный порядок @OrderBy применяется только во время выполнения при получении результата запроса. @OrderColumn приводит к постоянному упорядочению соответствующих данных.
- Первый метод использования составного ключа включает класс ключа целиком в класс сущности: @EmbeddedId указывает на поле составного первичного ключа, а @Embeddable объявляет класс составным ключом.
- Второй вариант использования оставляет поля первичного ключа непосредственно в классе сущности, а класс составного ключа служит лишь для поддержки: @IdClass(Passport.PassportKey.class)
@IdClass Допустим, у нас есть таблица с именем Account, и она имеет два столбца - accountNumber и accountType, которые формируют составной ключ. Чтобы обозначить оба этих поля как части составного ключа мы должны создать класс, например, AccountId с этими полями:
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:
@Entity
@IdClass(AccountId.class)
public class Account {
@Id
private String accountNumber;
@Id
private String accountType;
// other fields, getters and setters
}
@EmbeddedId
Является альтернативой аннотации @IdClass. Рассмотрим другой пример, в котором мы должны сохранить некоторую информацию о книге с заголовком и языком в качестве полей первичного ключа. В этом случае класс первичного ключа, BookId, должен быть аннотирован @Embeddable:
@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, чтобы получить доступ к полю, нам нужно из сущности обратиться к встраиваемому классу и потом к его полю:
SELECT account.accountNumber FROM Account account // с @IdClass
SELECT book.bookId.title FROM Book book // с @EmbeddedId
- @EmbeddedId более подробна, чем @IdClass, поскольку мы можем получить доступ ко всему объекту первичного ключа, используя метод доступа к полю в классе-сущности. Это также дает четкое представление о полях, которые являются частью составного ключа, поскольку все они агрегированы в классе, который доступен только через метод доступа к полям;
- @IdClass может быть предпочтительным выбором по сравнению с @EmbeddedId в ситуациях, когда класс составного первичного ключа поступает из другого модуля или устаревшего кода, а также когда мы не можем его изменить, например, чтобы установить аннотацию @EmbeddedId. Для таких сценариев, где мы не можем изменить класс составного ключа, аннотация @IdClass является единственным выходом;
- если мы собираемся получить доступ к частям составного ключа по отдельности, мы можем использовать @IdClass, но в тех местах, где мы часто используем полный идентификатор в качестве объекта, @EmbeddedId предпочтительнее.
Автоматически генерирует значение первичного ключа. Используется 4 стратегии (strategy = GenerationType.IDENTITY), Если мы не указываем значение явно, типом генерации по умолчанию является AUTO.
AUTO
значения определяется на основе типа атрибута первичного ключа. Выбирает стратегию генерации на основе конкретного диалекта базы данных. Для большинства популярных баз данных он выбирает GenerationType.SEQUENCE. IDENTITY
Указывает, что для генерации значения первичного ключа будет использоваться столбец IDENTITY, имеющийся в базе данных. Значения в столбце автоматически увеличиваются, что позволяет базе данных генерировать новое значение при каждой операции вставки. С точки зрения базы данных это очень эффективно, поскольку столбцы с автоинкрементом хорошо оптимизированы и не требуют каких-либо дополнительных операторов. Процесс инкремента (получения следующего) первичного ключа происходит вне текущей выполняемой транзакции, поэтому откат транзакции может в конечном итоге обнулить уже присвоенные значения (могут возникнуть пропуски значений).
Если мы используем Hibernate, то использование IDENTITY имеет существенный nедостаток. Так как Hibernate нужен первичный ключ для работы с managed-объектом в persistence context, а мы не можем узнать значение первичного ключа до выполнения инструкции INSERT, то Hibernate должен немедленно выполнить оператор INSERT, чтобы получить этот самый первичный ключ, сгенерированный БД. Только после этого у Hibernate будет возможность работать с сущностью в контексте персистентности, после чего выполнить операцию persist. Но Hibernate, в соответствии со своей идеологией, использует стратегию “транзакционная запись-после” (transactional write�behind), согласно которой он пытается максимально отложить сброс данных в БД из контекста персистентности, чтобы не делать много обращений к БД. Так как поведение при 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.
Самый простой способ - просто задать безымянную генерацию последовательности:
@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-генераторов), указав, например, имя последовательности и начальное значение:
@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 мы можем настроить этот тип генерации:
@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
-
@OrphanRemoval - управляет поведением осиротевшими сущностями. OrphanRemoval объявляет, что дочерние экземпляры сущностей должны быть удалены, когда на них не ссылаются из родителя.
-
CascadeType.REMOVE: «Дочерняя» сущность удаляется только тогда, когда ее «Родитель» удаляется.
Если указан orphanRemoval = true, удаленный экземпляр адреса автоматически удаляется. Если указан только cascade = CascadeType.REMOVE автоматическое действие не выполняется, поскольку удаление отношения не является удалить операцию.
ElementCollection - стандартная аннотация JPA, означает, что коллекция не является совокупностью объектов, а представляет собой набор простых типов (строки и т.д.) или набор встраиваемых элементов (класс, аннотированный с помощью @Embeddable).
Это также означает, что элементы полностью принадлежат содержащим объектам: они изменяются, когда объект изменяется, удаляется при удалении объекта и т.д. Они не могут иметь свой собственный жизненный цикл.
@ElementCollection указывается в классе сущности над полем коллекции базовых или встраиваемых типов. Все записи коллекции хранятся в отдельной таблице, то есть в итоге получаем две таблицы: одну для сущности, вторую для коллекции элементов. Конфигурация для таблицы коллекции элементов указывается с помощью аннотации @CollectionTable, которая используется для указания имени таблицы коллекции и JoinColumn, который ссылается на первичную таблицу.
@Entity
public class Customer {
@Id
@GeneratedValue
private int id;
private String name;
@ElementCollection
private List<String> phoneNumbers;
.............
}
Аннотация @ElementCollection похожа на отношение @OneToMany, за исключением того, что целью являются базовые и встраиваемые типы, а не сущности. Можно использовать аннотации @AttributeOverrides и @AttributeOverride для настройки отображения в таблице полей базовых или встраиваемых типов.
Коллекции могут иметь тип java.util.Map, которые состоят из ключа и значения. Для этого типа коллекций применяются следующие правила:
- Ключ или значение Map может быть базовым типом языка программирования Java, встраиваемым классом или сущностью.
- Если значение Map является встраиваемым классом или базовым типом, используйте аннотацию @ElementCollection. Пример.
- Если значение Map является сущностью, используйте аннотацию @OneToMany или @ManyToMany. Пример.
- Использовать тип Map только на одной стороне двунаправленной связи.
Аннотация @MapKeyColumn позволяет настроить столбец «ключ» в таблице Map. Аннотация @Column позволяет настроить столбец «значение» в таблице Map.
Использование коллекций элементов имеет один большой недостаток:элементы коллекции не имеют идентификатора, и Hibernate не может обращаться индивидуально к каждому элементу коллекции. Когда мы добавляем новый объект в коллекцию или удаляем из коллекции существующий элемент, Hibernate удаляет все строки из таблицы элементов и вставляет новые строки по одной для каждого элемента в коллекции. То есть при добавлении одного элемента в коллекцио Hibernate не добавит одну строку в таблицу коллекции, а очистит её и заполнит по новой всеми элементами.
Поэтому коллекции элементов следует использовать только для очень маленьких коллекций, чтобы Hibernate не выполнял слишком много операторов SQL. Во всех других случаях рекомендуется использовать коллекции сущностей с @OneToMany.
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
- IBM developerWorks®
- Документация к пакету java.sql
- Википедия - Уровень изолированности транзакции
-
Что такое JDBC?
-
В чем заключаются преимущества использования JDBC?
-
Что из себя представляет JDBC URL?
-
Из каких частей стоит JDBC?
-
Перечислите-основные-классы-и-интерфейсы-jdbc
-
Опишите основные этапы работы с базой данных с использованием JDBC.
-
Перечислите основные типы данных используемые в JDBC. Как они связаны с типами Java?
-
Как зарегистрировать драйвер JDBC?
-
Как установить соединение с базой данных?
-
Какие уровни изоляции транзакций поддерживаются в JDBC?
-
При помощи чего формируются запросы к базе данных?
-
Чем отличается Statement от PreparedStatement?
-
Как осуществляется запрос к базе данных и обработка результатов?
-
Как вызвать хранимую процедуру?
-
Как закрыть соединение с базой данных?
-
ANSI SQL
-
Основные элементы баз данных – таблицы, процедуры, функции, констрейнты и т.д..
-
Как вы понимаете null в базах данных?
-
Агрегатные функции, как они работают с null. Не забудьте о group by и having
-
Каким образом лучше добавлять большое количество записей в таблицу?
-
Что такое первая нормальная форма и процесс нормализации? Какие бывают нормальные формы?
-
В чем смысл индекса СУБД, как они устроены, как хранятся? Как бы вы реализовали тот же функционал?
-
Что такое JDBC API и когда его используют?
-
Что такое JDBC Driver и какие различные типы драйверов JDBC вы знаете?
-
Как JDBC API помогает достичь слабой связи между Java программой и JDBC Drivers API?
-
Что такое JDBC Connection? Покажите шаги для подключения программы к базе данных.
-
Как используется JDBC DriverManager class?
-
Как получить информацию о сервере базы данных из java программы?
-
Что такое JDBC Statement?
-
Какие различия между execute, executeQuery, executeUpdate?
-
Что такое JDBC PreparedStatement?
-
Как установить NULL значения в JDBC PreparedStatement?
-
Как используется метод getGeneratedKeys() в Statement?
-
Какие преимущества в использовании PreparedStatement над Statement?
-
Какие есть ограничения PreparedStatement и как их преодолеть?
-
Что такое JDBC ResultSet?
-
Какие существуют различные типы JDBC ResultSet?
-
Как используются методы setFetchSize() и SetMaxRows() в Statement?
-
Как вызвать Stored Procedures используя JDBC API?
-
Что такое JDBC Batch Processing и какие его преимущества?
-
Что такое JDBC Transaction Management и зачем он нужен?
-
Как откатить JDBC транзакцию?
-
Что такое JDBC Savepoint и как он используется?
-
Расскажите о JDBC DataSource. Какие преимущества он дает?
-
Как создать JDBC пул соединений используя JDBC DataSource и JNDI в Apache Tomcat Server?
-
Расскажите про Apache DBCP API.
-
Какие вы знаете уровни изоляции соединений в JDBC?
-
Что вы знаете о JDBC RowSet? Какие существуют различные типы RowSet?
-
В чем разница между ResultSet и RowSet?
-
Приведите пример наиболее распространенных исключений в JDBC.
-
Расскажите о типах данных CLOB и BLOB в JDBC.
-
Что вы знаете о «грязном чтении» (dirty read) в JDBC? Какой уровень изоляции предотвращает этот тип чтения?
-
Какие есть две фазы commit?
-
Приведите пример различных типов блокировки в JDBC.
-
Как вы понимаете DDL и DML выражения?
-
Какая разница между java.util.Date и java.sql.Date?
-
Как вставить изображение или необработанные данные в базу данных?
-
Что вы можете рассказать о фантомном чтении? Какой уровень изоляции его предотвращает?
-
Что такое SQL Warning? Как возвратить SQL предупреждения в JDBC программе?
-
Как запустить Oracle Stored Procedure с объектами базы данных IN/OUT?
-
Приведите пример возникновения java.sql.SQLException: No suitable driver found.
-
Best Practices в JDBC.
-
ANSI SQL - В 1986 году первый стандарт языка SQL был принят ANSI (American National Standards Institute).
-
Основные элементы баз данных – таблицы, процедуры, функции, констрейнты и т.д. Констрейнт - ограничение ссылочной целостности,либо объявление первичного ключа. Внешний ключ также является ограничением CONSTRAINT и отображает связь между двумя таблицами. Внешний ключ может ссылаться только на первичный ключ другой таблицы или на ограничение уникальности. Это значит, что после ключевого слова REFERENCES должно быть имя таблицы и в скобках можно указывать только первичный ключ или поле с ограничением UNIQUE. Другие поля указывать нельзя.
-
Констрейнты: как вы понимаете null в базах данных. null - это когда нет данных в поле. При создании первичного ключа, необходимо еще также накладывать дополнительное ограничение ссылочной целостности на домены, входящие в первичный ключ NOT NULL. Подробнее, см. CREATE DOMAIN
-
Агрегатные функции, как они работают с null. Не забудьте о group by и having Агрегатные функции - Avg,min,max,count Агрегатные функции выполняют вычисление на наборе значений и возвращают одиночное значение. Агрегатные функции, за исключением COUNT, не учитывают значения NULL. Агрегатные функции часто используются в выражении GROUP BY инструкции SELECT. Агрегатные функции могут быть использованы в качестве выражений только в следующих случаях. --Список выбора инструкции SELECT (вложенный или внешний запрос). --Предложение HAVING. предложение HAVING применяется после группировки для определения аналогичного предиката, фильтрующего группы по значениям агрегатных функций. Это предложение необходимо для проверки значений, которые получены с помощью агрегатной функции не из отдельных строк источника записей, определенного в предложении FROM, а из групп таких строк. Поэтому такая проверка не может содержаться в предложении WHERE. SELECT model, COUNT(model) AS Qty_model, AVG(price) AS Avg_price FROM PC GROUP BY model HAVING AVG(price) < 800;
-
JDBC: Connection, Statement, PreparedStatement, CallableStatement, ResulSet, зачем каждая из этих сущностей нужна. Чем они являются: абстрактными классамм, конкретными классами или интерфейсами и почему. В JDBC есть три класса для посылки SQL-запросов в БД и три метода в интерфейсе Connection создают экземпляры этих классов. Эти классы и методы, которые их создают, перечислены ниже: Statement - создается методом createStatement. Объект Statement используется при простых SQL-запросах. PreparedStatement - создается методом prepareStatement. Объект PreparedStatement используется в SQL-запросах с одним или более входными параметрами (IN parameters). PreparedStatement содержит группу методов, устанавливающих значения входных параметров, которые отсылаются в БД при выполнении запроса. Экземпляры класса PreparedStatement расширяют (наследуются от) Statement и, таким образом, включают методы Statement. Объект PreparedStatement потенциально может быть более эффективным, чем Statement, так как он прекомпилируется и сохраняется для будущего использования. CallableStatement - создается методом prepareCall. Объекты CallableStatement используются для выполнения т.н. хранимых процедур - именованных групп SQL-запросов, наподобие вызова подпрограммы. Объект CallableStatement наследует методы обработки входных (IN) параметров из PreparedStatement, а также добавляет методы для обработки выходных (OUT) и входных-выходных (INOUT) параметров.
Нижеследующий список дает представление о том, какой именно из методов объекта Connection лучше использовать для создания различных SQL-запросов:
Метод createStatement используется для простых SQL-выражений (без параметров)
Метод prepareStatement используется для SQL-выражений с одним или более входным (IN-) параметром или простых SQL-выражений, которые исполняются часто
Метод prepareCall используется для вызова хранимой процедуры
это все интерфейсы
public List<LabelValue> getAllProductsAsLabelValue(WebDTO webDTO){
if(webDTO == null){
return null;
}
PreparedStatement preparedStatement = null;
ResultSet rs = null;
Connection connection = Connections.getJNDIConnection();
try {
StringBuilder sqlClause = new StringBuilder(SELECT_ALL_PRODUCTS_LIKE_LABEL_VALUE);
String cityUid = webDTO.getCityUid();
preparedStatement = connection.prepareStatement(sqlClause.toString());
preparedStatement.setString(1,cityUid);
rs = preparedStatement.executeQuery();
List<LabelValue> result = null;
if (rs != null) {
result = new ArrayList<LabelValue>();
while (rs.next()) {
LabelValue model = new LabelValue();
String value = rs.getString("value");
model.setValue(value);
model.setLabel(value +"; " + rs.getString("label"));
result.add(model);
}
}
return result;
} catch (SQLException e) {
log.error("Failed to execute a query", e);
e.printStackTrace();
} finally {
closeAfterRaw(connection, preparedStatement, rs, log);
}
return null;
}
-
Как создать соединение в JDBC? public static Connection getJNDIConnection(){ try { String DATASOURCE_CONTEXT = "java:comp/env/jdbc/TeplopolDataSource";
try { Context initialContext = new InitialContext(); if ( initialContext == null){ log("JNDI problem. Cannot get InitialContext."); } DataSource datasource = (DataSource)initialContext.lookup(DATASOURCE_CONTEXT); if (datasource != null) { Connection connection = datasource.getConnection(); return connection; } else { log("Failed to lookup datasource."); } } catch ( NamingException ex ) { log("Cannot get connection: " + ex); } catch(SQLException ex){ log("Cannot get connection: " + ex); }
} catch (Exception e) { e.printStackTrace(); } return null; }
-
Каким образом лучше добавлять большое количество записей в таблицу? INSERT INTO
tbl_name
(field1
,field2
) VALUES (4,field1
*field1
) либо INSERT INTOusers_new
SELECT * FROMusers
WHEREcountry
= 'Russia' -
Транзакции и autocommit Транзакция состоит из одного или более выражений, которые поле выполнения либо все фиксируются (commit), либо все откатываются назад (rollback). При вызове метода commit или rollback текущая транзацкия заканчивается и начинается другая. Каждое новое соединение по умолчанию находится в режиме автофиксации (auto-commit), что означает автоматическую фиксацию (commit) транзакции после каждого запроса. В этом случае транзакция состоит из одного запроса. Если auto-commit запрещен, транзакция не заканчивается вплоть до явного вызова commit или rollback, включая, таким образом, все выражения, выполненные с момента последнего вызова commit или rollback. В этом случае все SQL-запросы в транзакции фиксируются или откатываются группой. Метод фиксации commit делает окончательными все изменения в БД, проделанные SQL-выражением, и снимает также все блокировки, установленные транзакцией. Метод rollback проигнорирует, "отбракует" эти изменения. Иногда пользователю нужно, чтобы какое-либо изменение не вступило в силу до тех пор, пока не вступит в силу предыдущее изменение. Этого можно достичь запрещением auto-commit и группировкой обоих запросов в одну транзакцию. Если оба изменения произошли успешно, то вызывается метод commit, который переносит эффект от этих изменений в БД; если одно или оба запроса не прошли, то вызывается метод rollback, который возвращает прежнее состояние БД. Большинство JDBC-драйверов поддерживают транзакции. На самом деле драйвер, соответствующий спецификации JDBC, обязан поддерживать их. Интерфейс DatabaseMetaData предоставляет информацию об уровнях изолированности транзакций, которые поддерживаются данной СУБД. try { conn.setAutoCommit (false); Statement s = conn.createStatement (); // передать деньги от одного человека другому s.executeUpdate ("UPDATE money SET amt = amt - 6 WHERE name = 'Eve'"); s.executeUpdate ("UPDATE money SET amt = amt + 6 WHERE name = 'Ida'"); s.close (); conn.commit (); conn.setAutoCommit (true); } catch (SQLException e) { System.err.println ("Transaction failed, rolling back."); Cookbook.printErrorMessage (e); // пустой обработчик исключений, если откат не удался try { conn.rollback (); conn.setAutoCommit (true); } catch (Exception e2) { } }
JTA представляет собой аббревиатуру Java Transaction API. Это API позволяет разграничить транзакции способом, независящим от реализации менеджера транзакций. J2EE SDK реализует менеджер транзакций при помощи Java Transaction Service (JTS). Но в коде эти методы не вызываются прямо. Вместо этого, вызываются JTA-методы, которые, в свою очередь, вызывают JTS-процедуры низкого уровня. JTA-транзакции управляются менеджером транзакций J2EE. Вы, вероятно, захотите использовать JTA-транзакцию потому, что она может обновлять нескольких баз данных от разных поставщиков. Менеджер транзакций конкретной DBMS может не работать с гетерогенными базами данных. Однако, менеджер транзакций J2EE имеет одно ограничение - он не поддерживает вложенных транзакций. Другими словами, он не может начать транзакцию для экземпляра пока предыдущая транзакция не закончится. Исходный код следующего примера расположен в каталоге j2eetutorial/examples/src/ejb/teller. Чтобы откомпилировать этот код, выполните команду ant teller. Для создания таблиц базы данных выполните команду ant create-bank-teller. Файл примера TellerApp.ear находится в каталоге j2eetutorial/examples/ears. Для того чтобы разграничить транзакцию, нужно вызвать методы begin, commit и rollback интерфейса javax.transaction.UserTransaction. Приведенный ниже код, взятый из класса TellerBean, демонстрирует методы UserTransaction. Вызовы begin и commit определяют границы обновлений базы данных. Если обновления заканчиваются неудачно, код вызывает метод roolback и генерирует EJBException.
public void withdrawCash(double amount) {
UserTransaction ut = context.getUserTransaction();
try { ut.begin(); updateChecking(amount); machineBalance -= amount; insertMachine(machineBalance); ut.commit(); } catch (Exception ex) { try { ut.rollback(); } catch (SystemException syex) { throw new EJBException ("Rollback failed: " + syex.getMessage()); } throw new EJBException ("Transaction failed: " + ex.getMessage()); } }
Есть несколько способов разрешения конфликтов между одновременно выполняющимися транзакциями. Пользователь может задать уровень изолированности, то есть уровень внимания, которое СУБД должна уделить при разрешении возможных конфликтов. Например, что будет, если одна транзакция изменит какое-либо значение, а вторая транзакция прочитает его перед тем как первая выполнит commit или rollback? Допустимо ли это, например, если это значение, будучи уже прочитанным второй транзакцией, окажется некорректным и будет откатано (rolled back) первой? Пользователь JDBC может указать СУБД на возможность чтения измененных значений до того, как выполнится commit ("грязное чтение", "dirty reads"). Это делается так (con - это соединение с БД):
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED); Чем выше уровень изолированности транзакций, тем больше внимания СУБД уделяет устранению конфликтов. Интерфейс Connection определяет пять таких уровней. Минимальный из них соответствует случаю, когда транзакции не поддерживаются вовсе, а максимальный - невозможности существования более одной транзакции в любой момент времени. Обычно, чем выше уровень изолированности, тем медленнее выполняется приложение (из-за избыточной блокировки и уменьшенной конкурентности пользователей). При выборе конкретного уровня изолированности разработчик должен найти золотую середину между потребностями в производительности и требованиями к целостности данных. Очевидно, что реально поддерживаемые уровни зависят от возможностей используемой СУБД. При создании объекта Connection его уровень изолированности зависит от драйвера или БД. Пользователь может вызвать метод setIsolationLevel, чтобы изменить уровень изолированности транзакций, и новое значение уровня будет установлено до конца сессии. Чтобы установить уровень изолированности только для одной транзакции, надо установить его перед выполнением транзакции и восстановить прежнее значение после ее завершения. Изменение уровня изолированности во время самой транзакции нежелательно, так как произойдет автоматический вызов commit, что повлечет за собой фиксацию изменений.
- Что такое первая нормальная форма и процесс нормализации? Какие бывают нормальные формы? Нормальная форма — свойство отношения в реляционной модели данных, характеризующее его с точки зрения избыточности, потенциально приводящей к логически ошибочным результатам выборки или изменения данных. Нормальная форма определяется как совокупность требований, которым должно удовлетворять отношение. Процесс преобразования отношений базы данных к виду, отвечающему нормальным формам, называется нормализацией. Нормализация предназначена для приведения структуры БД к виду, обеспечивающему минимальную логическую избыточность, и не имеет целью уменьшение или увеличение производительности работы или же уменьшение или увеличение физического объёма базы данных. [1] Конечной целью нормализации является уменьшение потенциальной противоречивости хранимой в базе данных информации. Как отмечает К. Дейт,[2] общее назначение процесса нормализации заключается в следующем:
исключение некоторых типов избыточности; устранение некоторых аномалий обновления; разработка проекта базы данных, который является достаточно «качественным» представлением реального мира, интуитивно понятен и может служить хорошей основой для последующего расширения; упрощение процедуры применения необходимых ограничений целостности. Устранение избыточности производится, как правило, за счёт декомпозиции отношений таким образом, чтобы в каждом отношении хранились только первичные факты (то есть факты, не выводимые из других хранимых фактов).
-
В чем смысл индекса СУБД, как они устроены, как хранятся? Как бы вы реализовали тот же функционал? Последний вопрос задают в случае, если нет четкого понимания индексов. Индекс состоит из набора страниц, узлов индекса, которые организованы в виде древовидной структуры — сбалансированного дерева. Эта структура является иерархической по своей природе и начинается с корневого узла на вершине иерархии и конечных узлов, листьев, в нижней части, как показано на рисунке: Когда вы формируете запрос на индексированный столбец, подсистема запросов начинает идти сверху от корневого узла и постепенно двигается вниз через промежуточные узлы, при этом каждый слой промежуточного уровня содержит более детальную информацию о данных. Подсистема запросов продолжает двигаться по узлам индекса до тех пор, пока не достигнет нижнего уровня с листьями индекса. К примеру, если вы ищете значение 123 в индексированном столбе, то подсистема запросов сначала на корневом уровне определит страницу на первом промежуточном (intermediate) уровне. В данном случае первой страница указывает на значение от 1 до 100, а вторая от 101 до 200, таким образом подсистема запросов обратится ко второй странице этого промежуточного уровня. Далее будет выяснено, что следует обратиться к третьей странице следующего промежуточного уровня.
-
Нарисуйте отношение Многие-ко-многим. Например: таблицы Авторы и Книги. У одного автора может быть несколько книг, и книга может быть написана несколькими авторами. Составьте SQL запрос на выборку книг определенного автора.