I have an eCommerce type app (Spring Boot) in progress and I'm using a HashMap<Item, Long> to keep track of items and item count in a shopping cart. When adding items to the cart the hashmap does not recognize similar items and makes a new entry in stead of adding to item count. I understand this is somehow related to Jpa and the way hashcode is generated. I tried the same situation as POJOs (no jpa or anything) and the key is recognized. I tried overriding equals(Object o) and hashCode() methods as shown below (to ignore the problematic list), but it does not help even though it now generates the same hashcode (as seen below). I would be very pleased to get this to work, thank you in advance!
Item:
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Item extends AbstractPersistable<Long> {
private String name;
private Long productNumber;
private Double price;
private Double discountPrice;
private Double discountFactor;
private Integer stock;
@Column(length = 10000)
private String description;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Image> images = new ArrayList<>();
private Boolean isHidden;
private Boolean isService;
@ManyToOne
private ItemCategory category;
public Item(String name, Double price, String description, int stock, ItemCategory category) {
this.name = name;
this.price = price;
this.description = description;
this.stock = stock;
this.category = category;
}
>>getters and setters<<
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Item item = (Item) o;
if (name != null ? !name.equals(item.name) : item.name != null) {
return false;
}
if (productNumber != null ? !productNumber.equals(item.productNumber) : item.productNumber != null) {
return false;
}
if (discountPrice != null ? !discountPrice.equals(item.discountPrice) : item.discountPrice != null) {
return false;
}
if (discountFactor != null ? !discountFactor.equals(item.discountFactor) : item.discountFactor != null) {
return false;
}
if (stock != null ? !stock.equals(item.stock) : item.stock != null) {
return false;
}
if (description != null ? !description.equals(item.description) : item.description != null) {
return false;
}
if (isHidden != null ? !isHidden.equals(item.isHidden) : item.isHidden != null) {
return false;
}
if (isService != null ? !isService.equals(item.isService) : item.isService != null) {
return false;
}
return category != null ? category.equals(item.category) : item.category == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result (productNumber != null ? productNumber.hashCode() : 0);
result = 31 * result (discountPrice != null ? discountPrice.hashCode() : 0);
result = 31 * result (discountFactor != null ? discountFactor.hashCode() : 0);
result = 31 * result (stock != null ? stock.hashCode() : 0);
result = 31 * result (description != null ? description.hashCode() : 0);
result = 31 * result (isHidden != null ? isHidden.hashCode() : 0);
result = 31 * result (isService != null ? isService.hashCode() : 0);
result = 31 * result (category != null ? category.hashCode() : 0);
return result;
}
Image:
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Image extends AbstractPersistable<Long> {
@Lob
@Basic(fetch = FetchType.LAZY)
private byte[] content;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
private Boolean isMainPicture;
private int ordinal;
}
ShoppingCart:
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
private Map<Item, Long> items = new HashMap<>();
public void addToCart(Item item) {
Long itemCount = this.items.getOrDefault(item, 0L) 1L;
this.items.put(item, itemCount);
}
public void removeFromCart(Item item) {
if (this.items.get(item) == 1L) {
this.items.remove(item);
return;
}
Long itemCount = this.items.get(item) - 1L;
this.items.put(item, itemCount);
}
public Double getSum() {
return this.items.keySet().stream()
.map(item -> (item.getPrice() * this.items.get(item)))
.reduce(0.0, (currentSum, nextCost) -> currentSum nextCost);
}
}
viewCart method in @Controller:
@GetMapping("/cart")
public String viewCart(Model model) {
System.out.println("!!!!!!!!!!!!!!!!!!");
shoppingCart.getItems().keySet().stream().forEach(key -> System.out.println(key.toString() "; hashcode = " key.hashCode()));
model.addAttribute("items", shoppingCart.getItems());
return "cart";
}
Console after viewCart method with two similar Items in the HashMap:
!!!!!!!!!!!!!!!!!!
Entity of type mortar.euroshopper.eCommerceApplication.Item with id: 6; hashcode = 34943583
Entity of type mortar.euroshopper.eCommerceApplication.Item with id: 6; hashcode = 34943583
CodePudding user response:
Thanks for M. Deinum for the comment that fixed the problem! Instructions found here: https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/