В Java с JPA и EclipseLink. Допустим, у меня есть 3 таблицы: Поставщик, Заказ и Клиент.
Я хочу, чтобы все клиенты, у которых есть заказ от указанного поставщика. Скажем, поставщики и клиенты не связаны напрямую. Их можно связать только путем присоединения через заказы:
В JPA, как только вы настроите свою модель/сущность, вы сможете сделать
EntityManager.find(Supplier.class, 1)
Это вернет поставщика и его заказы, оттуда мы сможем перейти к заказам и связать всех клиентов с этим поставщиком. Но таким способом извлекаются все данные, а нам нужны только клиенты и ничего больше, это создает нагрузку на БД и пропускную способность клиента.
Какой лучший способ JPA, JPQL получить только те данные о клиентах, которые связаны с поставщиком? Я знаю, что могу выполнить необработанный запрос, но это противоречит цели JPA.
Я вижу, спасибо, не могли бы вы дать мне пример кода того, как это вернет список клиентов, что, если бы мне нужны были и клиенты, и заказы?
Предположим, что вы определили связь @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();
Хм, спасибо, это не совсем то, что я ищу, мне также нужны данные о клиентах.
Если бы мы пошли по глупому пути и сделали бы 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? Как бы это поместило его в список?
o.customer
предоставляет вам список Customer
объектов со всеми их данными. Запрос JPQL автоматически преобразуется в объединения.
Добавлена Java-часть, которая превращает его в список.
Спасибо, это полностью решает мою проблему. Однако просто из любопытства: как бы выглядел JPQL, если бы мне нужны были и данные о клиентах, и данные о заказах?
Это было бы просто select o from Order o where o.supplier.id = :supplierId
. Если у ManyToOne
Клиента есть FetchType.EAGER
, объект Customer
будет заполнен автоматически. Однако не всегда используйте EAGER
. Например, если у вас есть @OneToMany
от Supplier
до Order
и есть тысячи заказов, использование EAGER
будет извлекать все заказы каждый раз, когда вы получаете поставщика. Я думаю, что по умолчанию это EAGER
, поэтому убедитесь, что вы установили его на LAZY
.
В ответ добавлен код Java, чтобы сгруппировать заказы по клиентам для конкретного поставщика.
Спасибо, что показали получение заказов, но на самом деле в одном запросе я имел в виду что-то вроде этого: 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();
Это хороший способ?
На мой взгляд, возвращать нетипизированный Object[]
немного грязно. Я не думаю, что вам нужно идти по этому пути со своей проблемой, учитывая, что отношения очень чистые. В Заказе уже указан Клиент, поэтому я не понимаю, почему вам нужно запрашивать его отдельно. Все, что вам нужно, это извлечь его из Ордена, если он вам нужен отдельно.
Просто любопытно, возможно ли это: P, так что в любом случае сделать это без списка объектов/общего интерфейса/классов, я полагаю, невозможно? Лучше всегда приходить к тому, у кого есть другой, в данном случае порядок? Поэтому мне нужно запросить заказы, а затем просто использовать свойство заказа .customer :)
Конечно, вы можете делать объединения столько, сколько захотите. Я просто думаю, что в этом случае это чище и типобезопаснее, и данные в любом случае будут внутри Ордена. Если вы хотите использовать JPA через Spring Data JPA, у него также есть проекции, где вы можете иметь свой собственный объект, например CustomerOrder
, который содержит Customer
и Order
как параметры конструктора, и вы получаете List<CustomerOrder>
, но, на мой взгляд, немного бесполезно, ведь у Заказа есть Заказчик.
Я вижу большое спасибо за помощь, я очень ценю это! Есть ли у вас какие-нибудь хорошие руководства по JPA, книга или что-то еще, что я могу использовать, чтобы стать лучше?
В последнее время я ничего не просматривал, но этот кажется довольно полным: tutorialspoint.com/jpa/index.htm ... кроме того, вы можете использовать Spring Data JPA (вместо доступа к EntityManager
), он абстрагирует вас из множества шаблонов.
JPQL также поддерживает соединения, например. что-то вроде
SELECT c FROM Suppliers s JOIN s.orders o JOIN o.customers c WHERE s.id = 1
(или даже возможно обратное). Я почти уверен, что критерии API позволят вам написать что-то подобное.