1. Обзор
В этом учебнике мы будем смотреть на различные типы присоединения поддерживается JPA .
Для этого мы будем использовать JP’L, язык запросов для JPA.
2. Пример модели данных
Давайте посмотрим на нашу модель выборочных данных, которую мы будем использовать в примерах.
Во-первых, мы создадим Сотрудник сущность:
@Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private int age; @ManyToOne private Department department; @OneToMany(mappedBy = "employee") private Listphones; // getters and setters... }
Каждая Сотрудник будет назначен только один Департамент :
@Entity public class Department { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String name; @OneToMany(mappedBy = "department") private Listemployees; // getters and setters... }
Наконец, каждый Сотрудник будет иметь несколько Телефонная секунда:
@Entity public class Phone { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String number; @ManyToOne private Employee employee; // getters and setters... }
3. Внутренние соединения
Начнем с внутреннего соединения. Когда две или более сущностей внутренне соединены, в результате собираются только записи, которые соответствуют состоянию присоединения.
3.1. Неявное внутреннее присоединение к одной ценной навигации Ассоциации
Внутренние соединения могут быть подразумеваемый. Как следует из названия, разработчик не указывает неявные внутренние соединения . Всякий раз, когда мы перемещаемся по одной ценной ассоциации, JPA автоматически создает неявное присоединение:
@Test public void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInnerJoin() { TypedQueryquery = entityManager.createQuery( "SELECT e.department FROM Employee e", Department.class); List resultList = query.getResultList(); // Assertions... }
Вот, Сотрудник организация имеет отношения «много к одному» с Департамент сущность. Если мы перейдите от Сотрудник лица к ее Департамент – указание e.department – Мы будем ориентироваться в одной ценной ассоциации. В результате, JPA создаст внутреннее присоединение. Кроме того, условие присоединения будет получено из отображения метаданных.
3.2. Явное внутреннее присоединение к Ассоциации с одной стоимостью
Далее, мы будем смотреть на явные внутренний соединяется там, где мы используем ключевое слово JOIN в нашем Запрос на JP’L :
@Test public void whenJoinKeywordIsUsed_thenCreatesExplicitInnerJoin() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Employee e JOIN e.department d", Department.class); List resultList = query.getResultList(); // Assertions... }
В этом запросе мы указали ключевое слово JOIN и связанное с ним Департамент сущность в пункте FROM , в то время как в предыдущем они не были указаны вообще. Однако, помимо этой синтаксической разницы, полученные запросы S’L будут очень похожи.
Мы также можем указать дополнительное ключевое слово INNER:
@Test public void whenInnerJoinKeywordIsUsed_thenCreatesExplicitInnerJoin() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Employee e INNER JOIN e.department d", Department.class); List resultList = query.getResultList(); // Assertions... }
Так как JPA будет неявно внутреннее присоединение, когда мы должны быть явными?
Во-первых, JPA создает неявное внутреннее присоединение только тогда, когда мы указать выражение пути. Например, когда мы хотим выбрать только Сотрудник s, которые имеют Департамент и мы не используем выражение пути – e.department – , мы должны использовать ключевое слово JOIN в нашем запросе.
Во-вторых, когда мы явные, это может быть легче узнать, что происходит.
3.3. Явное внутреннее присоединение к ассоциациям, ценным для коллекции
Другое место мы должны быть явными с коллекцио-ценными ассоциациями.
Если мы посмотрим на нашу модель данных, Сотрудник имеет отношения один к многим с Телефонная . Как и в предыдущем примере, мы можем попытаться написать аналогичный запрос:
SELECT e.phones FROM Employee e
Но это не совсем сработает как, может быть, мы намеревались. С выбранной ассоциации – e.phones – ценится коллекция, Мы получим список Коллекционая s, вместо Телефонная сущностей :
@Test public void whenCollectionValuedAssociationIsSpecifiedInSelect_ThenReturnsCollections() { TypedQueryquery = entityManager.createQuery( "SELECT e.phones FROM Employee e", Collection.class); List resultList = query.getResultList(); //Assertions }
Кроме того, если мы хотим, чтобы Телефонная сущностей в пункте ГДЕ, JPA не позволит этого. Это потому, выражение пути не может продолжаться из ассоциации, ценной для . Так, например, e.phones.number не действителен .
Вместо этого мы должны создать явное внутреннее присоединение и создать псевдоним для Телефонная сущность. Тогда мы можем указать Телефонная сущности в пункте SELECT или WHERE:
@Test public void whenCollectionValuedAssociationIsJoined_ThenCanSelect() { TypedQueryquery = entityManager.createQuery( "SELECT ph FROM Employee e JOIN e.phones ph WHERE ph LIKE '1%'", Phone.class); List resultList = query.getResultList(); // Assertions... }
4. Внешнее присоединение
Когда два или более сущностей находятся на внешней линии, записи, удовлетворяемые состоянию присоединения, а также записи в левой сущности собираются в результате:
@Test public void whenLeftKeywordIsSpecified_thenCreatesOuterJoinAndIncludesNonMatched() { TypedQueryquery = entityManager.createQuery( "SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e", Department.class); List resultList = query.getResultList(); // Assertions... }
Здесь результат будет содержать Департамент s, которые связаны Сотрудник s, а также те, которые не имеют.
Это также называется левым внешним присоединением. JPA не предоставляет правильные соединения где мы также собираем непревзойденные записи из нужной сущности. Хотя мы можем имитировать правильные соединения путем замены сущностей в пункте FROM.
5. Присоединяется к пункту ГДЕ
5.1. С условием
Мы можем перечислить две организации в пункте FROM и затем укажите условие присоединения в пункте WHERE .
Это может быть удобно, особенно когда иностранные ключи уровня базы данных не на месте:
@Test public void whenEntitiesAreListedInFromAndMatchedInWhere_ThenCreatesJoin() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Employee e, Department d WHERE e.department = d", Department.class); List resultList = query.getResultList(); // Assertions... }
Вот, мы присоединяемся к Сотрудник и Департамент сущностей, но на этот раз с указанием условия в пункте WHERE.
5.2. Без состояния (декартовая продукция)
Аналогичным образом, мы можем перечислить две сущности в пункте FROM, не указывая каких-либо условий . В этом случае Мы вернем тебе декартовую . Это означает, что каждая запись в первой сущности в паре с любой другой записью во второй сущности:
@Test public void whenEntitiesAreListedInFrom_ThenCreatesCartesianProduct() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Employee e, Department d", Department.class); List resultList = query.getResultList(); // Assertions... }
Как мы можем догадаться, такого рода запросы не будут работать хорошо.
6. Несколько соединений
До сих пор мы использовали две сущности для выполнения соединений, но это не правило. Мы также можем присоединиться к нескольким сущностям в одном запросе :
@Test public void whenMultipleEntitiesAreListedWithJoin_ThenCreatesMultipleJoins() { TypedQueryquery = entityManager.createQuery( "SELECT ph FROM Employee e JOIN e.department d JOIN e.phones ph WHERE d.name IS NOT NULL", Phone.class); List resultList = query.getResultList(); // Assertions... }
Здесь мы выбираем все Телефоны из всех Сотрудники которые имеют отдел. Как и другие внутренние соединения, мы не указываем условия, так как JPA извлекает эту информацию из метаданных отображения.
7. Получить соединения
Теперь, давайте поговорим о принести присоединяется. Его основное использование для получение ленивых загруженных ассоциаций с нетерпением для текущего запроса .
Здесь мы охотно загрузим Сотрудник s ассоциация:
@Test public void whenFetchKeywordIsSpecified_ThenCreatesFetchJoin() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Department d JOIN FETCH d.employees", Department.class); List resultList = query.getResultList(); // Assertions... }
Хотя этот запрос очень похож на другие запросы, есть одно отличие, и это то, что Сотрудник s охотно загружаются . Это значит, что однажды мы позвоним getResultList в тесте выше, Департамент субъекты будут иметь свои сотрудники поле загружено, тем самым спасая нас еще одну поездку в базу данных.
Но имейте в виду, что память компромисс . Мы можем быть более эффективными, потому что мы выполнили только один запрос, но мы также загрузили все Департамент s и их сотрудников в памяти сразу.
Мы также можем выполнить внешнее присоединение извлечения таким же образом, как и внешние соединения, где мы собираем записи из левого объекта, которые не соответствуют состоянию соединения. И, кроме того, он охотно загружает указанную ассоциацию:
@Test public void whenLeftAndFetchKeywordsAreSpecified_ThenCreatesOuterFetchJoin() { TypedQueryquery = entityManager.createQuery( "SELECT d FROM Department d LEFT JOIN FETCH d.employees", Department.class); List resultList = query.getResultList(); // Assertions... }
8. Резюме
В этой статье мы рассмотрели типы присоединения JPA.
Как всегда, вы можете проверить все образцы для этого и других учебников более на GitHub .