У меня есть две сущности: Customer
и CustomerOrder
. Зависимость между ними One-to-Many. В файле с репозиторием я создаю запрос, который должен:
active
имеют значение true
и пользователь существуетactive
равны false
.Запрос, который представлен ниже, он работает с первым пунктом из списка выше, но не с двумя оставшимися. Как правильно должен писаться запрос к БД, чтобы все пункты могли выполняться?
Запрос:
@Query("from Customer cs left join fetch cs.customerOrder custOrd " +
"where custOrd.active = true")
Customer findByCustomerId(Long id);
Ниже будет приведен пример таблиц и ответы в зависимости от запроса.
Customer.java:
@Entity
@Getter
@Setter
@Table
@ToString
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private String surname;
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<CustomerOrder> customerOrders = new ArrayList<>();
public void addCustomerOrder(CustomerOrder customerOrder) {
customerOrders.add(customerOrder);
customerOrder.setCustomer(this);
}
public void removeCustomerOrder(CustomerOrder customerOrder) {
customerOrders.remove(customerOrder);
customerOrder.setCustomer(null);
}
// equals() и hashCode()
}
CustomerOrder.java:
@Entity
@Getter
@Setter
@Table
@ToString
@NoArgsConstructor
public class CustomerOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private boolean active;
private String orderNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="customer_id")
private Customer customer;
// equals() и hashCode()
}
Customer:
table: customer
+----+------+---------+
| id | name | surname |
+----+------+---------+
| 1 | John | Smith |
| 2 | Mary | Johnson |
| 3 | Sam | Brown |
+----+------+---------+
CustomerOrder:
table: customer_order
+----+--------+--------------+-------------+
| id | active | order_number | customer_id |
+----+--------+--------------+-------------+
| 1 | true | AE123 | 1 |
| 2 | false | AE456 | 1 |
| 3 | false | AE567 | 2 |
| 4 | false | AE890 | 2 |
+----+--------+--------------+-------------+
findByCustomerId(1) [John]:
возвращается Customer John
и прикрепленных к нему CustomerOrder
с id: 2
findByCustomerId(2) [Mary]:
возвращается Customer Mary
. customer.getCustomerOrder().size() должен быть равен 0.
findByCustomerId(3) [Sam]:
возвращается Customer Sam
. customer.getCustomerOrder().size() должен быть равен 0.
Вы выбрали не тот инструмент, решение задачи лежит в другой плоскости.
Для начала надо разобраться с требованиями. Проведем эксперимент: будем последовательно увеличивать колличество CustomerOrder
и смотреть как это влияет на конечный результат.
Предположим сначала, что у Customer
нет ни одного CustomerOrder
. В таком случае данный Customer
должен быть выбран по п.2 требований.
Добавим к данному Customer
один CustomerOrder
:
Если у этого CustomerOrder
active == false
, то данный Customer
должен быть выбран по п.3 требований.
Если у этого CustomerOrder
active == true
, то данный Customer
должен быть выбран по п.1 требований.
Продолжим и дальше добавлять новые CustomerOrder
. Предположим, что у Customer
уже есть N CustomerOrder
и для всех из них верно, что active == false
. Данный Customer
выбирается по п.3 требований. Теперь добавим новый CustomerOrder
.
Если у нового CustomerOrder
active == false
, то данный Customer
должен быть выбран по п.3 тербований.
Если у нового CustomerOrder
active == true
, то данный Customer
должен быть выбран по п.1 требований.
Становится понятно, что не имеет значения какое колличество CustomerOrder
(и с какими атрибутами) есть у Customer
, любой Customer
подойдет под требования запроса.
Поэтому задача сводится не к поиску Customer
, а к фильтрации CustomerOrder
. Сама фильтрация может быть решена множеством путей:
@Where
из Hibernate:public class Customer {
// ...
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<CustomerOrder> customerOrders = new ArrayList<>();
public void addCustomerOrder(CustomerOrder customerOrder) {
customerOrders.add(customerOrder);
customerOrder.setCustomer(this);
}
public void removeCustomerOrder(CustomerOrder customerOrder) {
customerOrders.remove(customerOrder);
customerOrder.setCustomer(null);
}
// Дополнительная readonly-коллекция заказов, удовлетворяющих дополнительным условиям
@OneToMany
@JoinColumn(name = "customer_id", insertable = false, updatable = false, nullable = false)
@Where(clause = "active = true")
private List<CustomerOrder> customerOrdersSample;
// ...
}
public class CustomerService {
// ...
private final CustomerRepository customerRepository;
// ...
public List<CustomerOrder> getCustomerOrdersByCustomerId(Long customerId) {
return customerRepository
.findById(customerId)
.map(Customer::getCustomerOrders)
.orElse(Collections.emptyList())
.stream()
.filter(CustomerOrder::isActive)
.collect(Collectors.toList());
}
}
Запрос в репозиторий с CustomerOrder
на выбор всех сущностей с active == true
и принадлежащих определенному Customer
тоже будет являться решением задачи.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Мне нужно получить ширину и высоту экрана на устройстве, на котором сейчас выполняется приложениеКак мне это сделать?
Как мне дождаться выполнение асинхронного метода? Есть такой метод ServergetData(DataListener listener); Данные загружаются в отдельном потоке, и по завершению...
Здесь на странице регистрации я хочу передать допустим имя в следующее окно вот так: