Как эффективно объединять таблицы?

В Java с JPA и EclipseLink. Допустим, у меня есть 3 таблицы: Поставщик, Заказ и Клиент.

Я хочу, чтобы все клиенты, у которых есть заказ от указанного поставщика. Скажем, поставщики и клиенты не связаны напрямую. Их можно связать только путем присоединения через заказы:

  • В заказе может быть один клиент.
  • У клиента может быть несколько заказов
  • Заказ также привязан к поставщику.
  • У поставщика может быть много заказов.
  • В заказе может быть только 1 поставщик.

Один из способов сделать это:

В JPA, как только вы настроите свою модель/сущность, вы сможете сделать

EntityManager.find(Supplier.class, 1)

Это вернет поставщика и его заказы, оттуда мы сможем перейти к заказам и связать всех клиентов с этим поставщиком. Но таким способом извлекаются все данные, а нам нужны только клиенты и ничего больше, это создает нагрузку на БД и пропускную способность клиента.

Какой лучший способ JPA, JPQL получить только те данные о клиентах, которые связаны с поставщиком? Я знаю, что могу выполнить необработанный запрос, но это противоречит цели JPA.

JPQL также поддерживает соединения, например. что-то вроде SELECT c FROM Suppliers s JOIN s.orders o JOIN o.customers c WHERE s.id = 1 (или даже возможно обратное). Я почти уверен, что критерии API позволят вам написать что-то подобное.

Thomas 30.04.2024 17:54

Я вижу, спасибо, не могли бы вы дать мне пример кода того, как это вернет список клиентов, что, если бы мне нужны были и клиенты, и заказы?

deantre.s 30.04.2024 18:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Предположим, что вы определили связь @ManyToOne в Order как с Customer, так и с Supplier, например:

@Entity
@Table("order")
public class Order {

   @Id
   @GeneratedValue
   private Long id;

   @ManyToOne
   @JoinColumn(name = "customer_id")
   private Customer customer;

   @ManyToOne
   @JoinColumn(name = "supplier_id")
   private Supplier supplier;
   
   ...
}

У вас может быть такой запрос JPQL:

List<Customer> customers = entityManager.createQuery("select distinct o.customer from Order o where o.supplier.id = :supplierId").getResultList();

Если вам также нужны данные о заказах, вы можете получить только заказы следующим образом, а затем передать их в потоковую передачу для получения клиентов:

List<Order> orders = entityManager.createQuery("select o from Order o where o.supplier.id = :supplierId").getResultList();

Map<Customer, List<Order>> customerOrders = orders.stream().collect(Collectors.groupingBy(Order::getCustomer));

//if you still need the customers on their own
Set<Customer> customers = customerOrders.keySet();

Хм, спасибо, это не совсем то, что я ищу, мне также нужны данные о клиентах.

deantre.s 30.04.2024 18:03

Если бы мы пошли по глупому пути и сделали бы SELECT distinct o.customer FROM supplier s JOIN order o ON o.supplierid = s.id JOIN customer c ON c.id = o.customerid WHERE supplierid = :supplierd Возможно ли это в JPQL? Как бы это поместило его в список?

deantre.s 30.04.2024 18:04
o.customer предоставляет вам список Customer объектов со всеми их данными. Запрос JPQL автоматически преобразуется в объединения.
jbx 30.04.2024 18:16

Добавлена ​​Java-часть, которая превращает его в список.

jbx 30.04.2024 18:23

Спасибо, это полностью решает мою проблему. Однако просто из любопытства: как бы выглядел JPQL, если бы мне нужны были и данные о клиентах, и данные о заказах?

deantre.s 30.04.2024 18:33

Это было бы просто select o from Order o where o.supplier.id = :supplierId. Если у ManyToOne Клиента есть FetchType.EAGER, объект Customer будет заполнен автоматически. Однако не всегда используйте EAGER. Например, если у вас есть @OneToMany от Supplier до Order и есть тысячи заказов, использование EAGER будет извлекать все заказы каждый раз, когда вы получаете поставщика. Я думаю, что по умолчанию это EAGER, поэтому убедитесь, что вы установили его на LAZY.

jbx 30.04.2024 18:39

В ответ добавлен код Java, чтобы сгруппировать заказы по клиентам для конкретного поставщика.

jbx 30.04.2024 18:44

Спасибо, что показали получение заказов, но на самом деле в одном запросе я имел в виду что-то вроде этого: Query query = em.createQuery("SELECT c, o FROM Customer c JOIN c.orders o WHERE o.supplier.id = :supplierId"); query.setParameter("supplierId", supplierId); // Execute the query and get the results List<Object[]> results = query.getResultList(); Это хороший способ?

deantre.s 30.04.2024 18:47

На мой взгляд, возвращать нетипизированный Object[] немного грязно. Я не думаю, что вам нужно идти по этому пути со своей проблемой, учитывая, что отношения очень чистые. В Заказе уже указан Клиент, поэтому я не понимаю, почему вам нужно запрашивать его отдельно. Все, что вам нужно, это извлечь его из Ордена, если он вам нужен отдельно.

jbx 30.04.2024 18:51

Просто любопытно, возможно ли это: P, так что в любом случае сделать это без списка объектов/общего интерфейса/классов, я полагаю, невозможно? Лучше всегда приходить к тому, у кого есть другой, в данном случае порядок? Поэтому мне нужно запросить заказы, а затем просто использовать свойство заказа .customer :)

deantre.s 30.04.2024 19:01

Конечно, вы можете делать объединения столько, сколько захотите. Я просто думаю, что в этом случае это чище и типобезопаснее, и данные в любом случае будут внутри Ордена. Если вы хотите использовать JPA через Spring Data JPA, у него также есть проекции, где вы можете иметь свой собственный объект, например CustomerOrder, который содержит Customer и Order как параметры конструктора, и вы получаете List<CustomerOrder>, но, на мой взгляд, немного бесполезно, ведь у Заказа есть Заказчик.

jbx 30.04.2024 19:12

Я вижу большое спасибо за помощь, я очень ценю это! Есть ли у вас какие-нибудь хорошие руководства по JPA, книга или что-то еще, что я могу использовать, чтобы стать лучше?

deantre.s 30.04.2024 21:56

В последнее время я ничего не просматривал, но этот кажется довольно полным: tutorialspoint.com/jpa/index.htm ... кроме того, вы можете использовать Spring Data JPA (вместо доступа к EntityManager), он абстрагирует вас из множества шаблонов.

jbx 02.05.2024 21:03

Другие вопросы по теме