[英]JPA/Hibernate: mapping a single element in an entity with an one-to-many relationship
[英]Collectors mapping() + reducing() - folding stream element having One-To-Many relationship with Java 8
我有以下域类的类:
@AllArgsConstructor
@Getter
@ToString
public static class Customer {
private String id;
private String name;
}
@Getter
public static class Order {
private String id;
private Set<OrderContent> orderContent = new HashSet<>();
private Customer customer;
public Order(String id, Customer customer) {
this.id = id;
this.customer = customer;
}
public Order addOrderContent(Product product, int amount) {
orderContent.add(new OrderContent(product, amount));
return this;
}
}
@AllArgsConstructor
@Getter
public static class Product {
private String name;
private BigDecimal price;
}
@AllArgsConstructor
@Getter
public static class OrderContent {
private Product product;
private Integer quantity;
}
因此,每个Order
都与一个Customer
关联,并与OrderContent
一对多关联。 每个OrderContent
都有属性: product和quantity 。
我想退回最有价值的客户,也就是所有下单中价值最高的客户。
让我们考虑下面的例子:
List<Order> orders = new ArrayList<>();
Customer customer1 = new Customer("1", "Bill");
Customer customer2 = new Customer("2", "John");
Product device1 = new Product("Device 1", new BigDecimal("30.00"));
Product device2 = new Product("Device 2", new BigDecimal("50.00"));
Product device3 = new Product("Device 3", new BigDecimal("110.00"));
orders.add(new Order("1", customer1).addOrderContent(device1 , 1));
orders.add(new Order("2", customer1).addOrderContent(device2 , 1));
orders.add(new Order("3", customer2).addOrderContent(device3, 1));
下面的代码无法编译。 问题出在收集器内部,因为mapping()
中 arguments 的类型不匹配。 如何正确实现这个逻辑?
我的代码:
Customer customer = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.mapping((Order order) -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(new BigDecimal(orderContent.getQuantity()))),
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
))
.entrySet().stream()
.max(Comparator.comparing(Map.Entry::getValue))
.map(Map.Entry::getKey).orElse(null);
你快到了。
唯一需要做的是正确处理一对多的转换——一个订单到这个订单中的产品价格。
代替收集器mapping()
将Order
object 转换为OrderContent
对象序列,您可以使用从 JDK 版本 9 开始可用的收集器flatMapping()
作为groupingBy()
的下游。
然后我们可以通过将 price 和 amount 相乘将每个OrderContent
转换为BigDecimal
,并像您一样使用 reduce reducing()
获得每个Customer
的总花费。
Customer mostValCust = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.flatMapping((Order order) -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(BigDecimal.valueOf(orderContent.getQuantity()))),
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
用 Java 8 实现它的方法之一是将累积每个Order
的整体价格的逻辑移到收集器reduction reducing()
中:
Customer mostValCust = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.reducing(
BigDecimal.ZERO,
order -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(BigDecimal.valueOf(orderContent.getQuantity())))
.reduce(BigDecimal.ZERO,BigDecimal::add),
BigDecimal::add)
))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.