Рубрики
Без рубрики

Типы присоединения JPA

Изучите различные типы присоединений, поддерживаемые JPA.

Автор оригинала: baeldung.

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 List phones;

    // getters and setters...
}

Каждая Сотрудник будет назначен только один Департамент :

@Entity
public class Department {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    @OneToMany(mappedBy = "department")
    private List employees;

    // 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() {
    TypedQuery query
      = 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() {
    TypedQuery query
      = 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() {
    TypedQuery query
      = 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() {
    TypedQuery query 
      = 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() {
    TypedQuery query 
      = 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() {
    TypedQuery query 
      = 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() {
    TypedQuery query 
      = 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() {
    TypedQuery query
      = entityManager.createQuery(
          "SELECT d FROM Employee e, Department d", Department.class);
    List resultList = query.getResultList();
    
    // Assertions...
}

Как мы можем догадаться, такого рода запросы не будут работать хорошо.

6. Несколько соединений

До сих пор мы использовали две сущности для выполнения соединений, но это не правило. Мы также можем присоединиться к нескольким сущностям в одном запросе :

@Test
public void whenMultipleEntitiesAreListedWithJoin_ThenCreatesMultipleJoins() {
    TypedQuery query
      = 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() {
    TypedQuery query 
      = 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() {
    TypedQuery query 
      = entityManager.createQuery(
          "SELECT d FROM Department d LEFT JOIN FETCH d.employees", Department.class);
    List resultList = query.getResultList();
    
    // Assertions...
}

8. Резюме

В этой статье мы рассмотрели типы присоединения JPA.

Как всегда, вы можете проверить все образцы для этого и других учебников более на GitHub .