0

I have a spring boot app which is basically the backend of a simple e-commerce site. There are four entities: product, order, customer and orderproduct. The last one has a composite key, made from the respective order and product entities. They have the respective counterparts in a postgres database. Here is the code for the classes:

  • order entity
@Entity
@Table(name = "orders")
public class Order {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<OrderProduct> products;
    
    @ManyToOne
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;
    
    @DecimalMin(value = "0", message = "Amount must be greater than or equal to 0")
    private BigDecimal amount;
    
    @PastOrPresent
    private LocalDate createdAt;
    
    public Order() {}

    public Order(Integer id, Set<OrderProduct> products) throws Exception {
        super();
        this.id = id;
        this.products = products;
        this.amount = calculateAmount();
    }
    
    // getters and setters
}
  • order dto:
public class OrderDto {

    private Integer id;
    
    private Set<OrderProduct> products;
    
    @DecimalMin(value = "0", message = "Amount must be greater than or equal to 0")
    private BigDecimal amount;
    
    @NotNull
    private CustomerDto customer;
    
    @PastOrPresent
    private LocalDate createdAt;
    
    public OrderDto() {
        // TODO Auto-generated constructor stub
    }
    
    public OrderDto(Order order) {
        super();
        this.id = order.getId();
        this.amount = order.getAmount();
    }
    
    public OrderDto(Order order, boolean lazy) {
        super();
        this.id = order.getId();
        this.amount = order.getAmount();
        this.customer = new CustomerDto(order.getCustomer(), true);
        this.createdAt = order.getCreatedAt();
        
        if (!lazy) {
            products = new HashSet<>();
            
            for (OrderProduct orderProduct : order.getProducts()) {
                products.add(orderProduct);
            }
        }
    }

    public OrderDto(Integer id, BigDecimal amount) {
        this.id = id;
        this.amount = amount;
    }

    public OrderDto(OrderDto order, boolean lazy) {
        super();
        this.id = order.getId();
        this.amount = order.getAmount();
        this.customer = new CustomerDto(order.getCustomer(), true);
        this.createdAt = order.getCreatedAt();
        
        if (!lazy) {
            products = new HashSet<>();
            
            for (OrderProduct orderProduct : order.getProducts()) {
                products.add(orderProduct);
            }
        }
    }

    // getters and setters
}
  • order service implementation:
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    OrderRepository orderRepository;
    
    private final OrderProductRepository orderProductRepository;
    
    @Autowired
    ModelMapper modelMapper;
    
    @Autowired
    private Validator validator;
    
    public OrderServiceImpl(OrderProductRepository orderProductRepository) {
        this.orderProductRepository = orderProductRepository;
    }

    @Override
    public OrderDto addOrder(Customer customer, Set<OrderProduct> products) throws Exception {
        for (OrderProduct orderProduct : products) {
            System.out.println(">> " + orderProduct.getProduct());
        }
        
        Order order = new Order();
        
        order.setCustomer(customer);
        order.setProducts(products);
        
        System.out.println(order.getId());
        
        order = orderRepository.save(modelMapper.map(order, Order.class));

        for (OrderProduct product : products) {
            System.out.println(product.getProduct().getId());
            
            product.setId(new OrderProductKey(order.getId(), product.getProduct().getId()));
            orderProductRepository.save(product);
        }
        
        return modelMapper.map(order, OrderDto.class);
    }


    @Override
    public Collection<OrderDto> getOrders() throws EmptyOrderListException {
        if (ObjectUtils.isEmpty(orderRepository.findAll())) {
            throw new EmptyOrderListException();
        }
        
        Collection<OrderDto> orders = new ArrayList<>();
        
        for (Order order : orderRepository.findAll()) {
            orders.add(new OrderDto((Order) order, false));
        }
            
        return orders;
    }
    
    @Override
    public Collection<OrderDto> getOrdersByClientId(Integer id) throws InvalidIdException, EmptyOrderListException {
        if (ObjectUtils.isEmpty(orderRepository.findAll())) {
            throw new EmptyOrderListException();
        }
        
        if (ObjectUtils.isEmpty(id)) {
            throw new InvalidIdException();
        }
        
        Collection<OrderDto> clientOrders = new ArrayList<>();
        
        for (Order order : orderRepository.findAll()) {
            if (order.getCustomer().getId() == id) {
                clientOrders.add(new OrderDto(order, false));
            }
        }
        
        return clientOrders;
    }

    @Override
    public void deleteOrder(Order order) throws EmptyOrderListException, OrderNotFoundException {
        if (ObjectUtils.isEmpty(order)) {
            throw new OrderNotFoundException();
        }
        
        if (ObjectUtils.isEmpty(orderRepository.findAll())) {
            throw new EmptyOrderListException();
        }
        
        orderRepository.delete(order);
    }

    @Override
    public void deleteOrderById(Integer id) throws EmptyOrderListException, InvalidIdException {
        if (ObjectUtils.isEmpty(id)) {
            throw new InvalidIdException();
        }
        if (ObjectUtils.isEmpty(orderRepository.findAll())) {
            throw new EmptyOrderListException();
        }
        
        orderRepository.deleteById(id);
    }

}
  • order controller:
@RestController
@CrossOrigin
@RequestMapping(path = "/order")
public class OrderController {
    
    @Autowired      
    OrderService orderService;
    
    @Autowired
    ModelMapper modelMapper;
    
    @PostMapping("/add")
    public ResponseEntity<?> addOrder(@RequestBody OrderDto order) {
        try {
            return new ResponseEntity<OrderDto>(
                    new OrderDto(
                            orderService.addOrder(modelMapper.map(order.getCustomer(), Customer.class), order.getProducts()),
                            true
                            ), 
                    HttpStatus.CREATED
                    );
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<Object>(e.getStackTrace(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @GetMapping("/all")
    public ResponseEntity<Collection<OrderDto>> getOrders() {
        try {
            return new ResponseEntity<Collection<OrderDto>>(orderService.getOrders(), HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @GetMapping("/by-client-id/{id}")
    public ResponseEntity<Collection<OrderDto>> getOrderByClientId(@PathVariable Integer id) {
        try {
            return new ResponseEntity<Collection<OrderDto>>(
                    orderService.getOrdersByClientId(id), 
                    HttpStatus.OK
                    );
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

}
  • orderproduct entity:
package com.shine.ecommerce.entity;
@Entity
@Table(name = "order_product")
public class OrderProduct {

    @EmbeddedId
    private OrderProductKey id;

    @ManyToOne
    @JoinColumn(name = "product_id", insertable = false, updatable = false)
    private Product product;

    @ManyToOne
    @JoinColumn(name = "order_id", insertable = false, updatable = false)
    private Order order;

    private Integer qty;
    
    public OrderProduct() {
        // TODO Auto-generated constructor stub
    }

    public OrderProduct(OrderProductKey id, Product product, Order order, Integer qty) {
        super();
        this.id = id;
        this.product = product;
        this.order = order;
        this.qty = qty;
    }
    
    public OrderProduct(Product product, Order order, Integer qty) {
        super();
        this.product = product;
        this.order = order;
        this.qty = qty;
    }
    // getters and setters
    
}
  • orderproductkey:
@SuppressWarnings("serial")
@Embeddable
public class OrderProductKey implements Serializable {
    
    @Column(name = "product_id")
    Integer productId;

    @Column(name = "order_id")
    Integer orderId;
    
    public OrderProductKey(Integer productId, Integer orderId) {
        this.productId = productId;
        this.orderId = orderId;
    }

}

Unfortunately, when I try the addOrder call in postman, it cannot do it and it throws me the error:

org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.shine.ecommerce.entity.OrderProduct#com.shine.ecommerce.entity.OrderProductKey@3933181]

What can be the problem? Are the classes written correctly? I am passing the following request body to postman, considering that the product has the fields id, name, price, qty and imageUrl, and the customer has the fields id, name and surname: (I have wrapped the product's id in brackets to try to solve another exception)

{
    "id": 0,
    "products": [
        {
            "id": {
                "id": 9
                },
            "name": "evidenzia tori",
            "price": 9.99,
            "qty": 3,
            "imageUrl": "https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.medioshopping.com%2Fmedioshopping_users_img%2FPiccol_1308397940.jpg&f=1&nofb=1&ipt=6b8d80cae0fda00f002a848c64f8ce0898c46be48302c70c6c885f8b9a9db0b5&ipo=images"
        },
        {
        "id": {
                "id": 1
                },
        "name": "luigi puppet",
        "price": 39.99,
        "qty": 3,
        "imageUrl": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdn.shopify.com%2Fs%2Ffiles%2F1%2F0360%2F5713%2Fproducts%2FLuigi_-_1_1024x1024.jpg%3Fv%3D1542123934&f=1&nofb=1&ipt=c6f27f594d7a035a931c6ec0a2be200f41b593bbd87d5e69c6012937fa95c274&ipo=images"
    }
    ],
        "customer": {
            "id": 1,
            "name": "John",
            "surname": "Cena"
        }
}

Is that correct considering how the application is written? Apart from this, before some adjustments the application throws me other errors because it seems that when calling addOrder the product and the order id are null. If you need to look something else, here is the link for my repository, the code is in the compositekey branch. I'm not that expert with spring, I cannot really figure out what is happening.

0 Answers0