JPA-запрос один ко многим без предложения соединения - PullRequest
0 голосов
/ 09 января 2020

Имеются две таблицы (сценарии для SQL Сервера) с отношением один ко многим:

CREATE TABLE address (
  id BIGINT IDENTITY (1, 1) NOT NULL,
  street VARCHAR(255),
  zip VARCHAR(32),
  city VARCHAR(255),  
  country VARCHAR(32),

  CONSTRAINT PK_address PRIMARY KEY NONCLUSTERED(id)
);
CREATE TABLE person (
  id BIGINT IDENTITY (1, 1) NOT NULL,
  address_id BIGINT NOT NULL,
  first_name VARCHAR(32),
  last_name VARCHAR(32),

  CONSTRAINT PK_person PRIMARY KEY NONCLUSTERED(id),
  CONSTRAINT FK_person_address FOREIGN KEY (address_id) REFERENCES address
); 

У человека есть ровно один адрес, но один и тот же адрес может быть связан с несколькими разными людьми.

Существует также репозиторий Spring JPA с одним явным запросом:

@Repository
public interface PersonJpaRepository extends JpaRepository<Person, Long> {

    List<Person> findByAddressId(Long addressId);
}

При использовании Hibernate в качестве поставщика JPA для этого метода поиска создается SQL, подобное этому:

select person0_.id, person0_.first_name, person0_.last_name
from person person0_ 
left outer join address address1_ on person0_.address_id=address1_.id 
where person0_.address_id=?

Поскольку я не заинтересован в каких-либо данных из таблицы address и я уже знаю address_id, похоже, нет реальной необходимости в объединении:

select person0_.id, person0_.first_name, person0_.last_name
from person person0_ 
where person0_.address_id=?

Этот запрос дает тот же результат.

Можно ли в любом случае заставить JPA / Hibernate создать SQL без объединения без явного определения запроса через @Query?

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

Да, вы можете! И я могу быть очень полезен, чтобы исключить объединение, потому что оно не вводит второе имя таблицы. Например, это может сделать ORDER намного проще, потому что нет необходимости называть таблицу.

Чтобы сделать ее более конкретной. Можно сделать HQL-SELCT и ограничить отношения ManyToOne , без какого-либо объединения в результате SQL:

вместо используя объединение в

session.createQuery("FROM Person person WHERE person.adress.id = 42")

, которое мы используем, можно использовать adress_id столбец

session.createQuery("FROM Person person WHERE person.adressId = 42")

. Это работает, если указать дополнительное поле adressId, которое используется только как отображение информация для Hibernate:

@Entity
@Access(AccessType.FIELD)
public class Person{
    @Id
    String id;

    @JoinColumn(name = "adress_id") 
    @ManyToOne(fetch = FetchType.LAZY)
    @Nullable
    public Adress adress;


    @Column(name = "adress_id", insertable = false, updatable = false)
    private String adressId;

 // [... street, zip, city, country]
}


@Entity
@Access(FIELD)
public class Adress{
    @Id
    String id;

   // [... firstName, lastName]
}

AccessType.FIELD не требуется (но мы можем оставить методы получения / установки в примере). FetchType.LAZY и @Nullable также необязательны, но дают понять, когда имеет смысл их использовать. Мы можем загрузить Person сущностей, которые имеют спецификацию c Address (мы знаем идентификатор адреса). Но нам не нужно соединение, потому что оно не нужно для предложения WHERE, а не для начальной выборки (адрес может быть получен ленивым).

1 голос
/ 09 января 2020

Вы не можете изменить поведение Hibernate, но вам не нужно заботиться о соединении.

База данных все равно оптимизирует запрос, потому что нет защиты от адреса таблицы.

Так что со стороны БД оба запроса одинаковы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...