Search code examples
springspring-bootbackendrelationshipone-to-one

One to one relationship doesn't work in my Spring Boot back-end


I'm developing an ecommerce backend in Spring boot. There is a 1:1 relationship between the two entities Order and Payment. At first, I create a Payment (with order field null), when the payment is succesful (suppose always in this case), I create the order related to the payment created. The problem is that the Order entity is created but it isn't connected with the Payment entity because when I try to print the order in the PaymentService it says that the order is stil null for that payment (even if, in createOrder method, I did payment.setOrder(o); as shown below). Moreover, if from the database I try to delete the payment, it tells me that the payment could not be removed because it is connected to the order, but strangely, when I try to print the order of the payment that I have created, the result is null.

enter image description here

OrderService:

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private CartRepository cartRepository;

    @Autowired
    private PaymentRepository paymentRepository;

    @Autowired
    private EntityManager entityManager;

    @Transactional(readOnly = false)
    public Order createOrder (int paymentId, int userId, int cartId) {
        System.out.println("UserId: "+userId);
        System.out.println("Payment: "+paymentId);
        System.out.println("cartId: "+cartId);
        if(userRepository.existsById(userId)){
            System.out.println("Exists");}
        User user = userRepository.findById(userId);
        if(user==null){
            System.out.println("User is null");
        }
        Payment payment = paymentRepository.findById(paymentId);
        System.out.println("Payment: "+payment);
        Cart c = cartRepository.findById(cartId);
        Order o = new Order();
        o.setPayment(payment);
        o.setCart(c);
        o.setBuyer(user);
        Order justAdded = orderRepository.save(o);
        entityManager.refresh(justAdded);
        entityManager.refresh(o);
        payment.setOrder(o);
        entityManager.refresh(payment);
        return justAdded;
    }
}

OrderController:

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    @ResponseStatus(code = HttpStatus.OK)
    public ResponseEntity<Order> createOrder (@RequestParam(required = false) int paymentId,
                                                    @RequestParam(required = false) int buyerId,
                                                    @RequestParam(required = false) int cartId){

            return new ResponseEntity<>(orderService.createOrder(paymentId, buyerId, cartId), HttpStatus.OK);

    }

PaymentService:

@Service
public class PaymentService {

    @Autowired
    private PaymentRepository paymentRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private EntityManager entityManager;

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public Payment addPayment(Payment p) throws PaymentAlreadyExistsException, IncorrectCardNumberException, IncorrectCvvException{//
        if(paymentRepository.existsByCardNumber(p.getCardNumber())){
            throw new PaymentAlreadyExistsException();
        }
        if(p.getCardNumber().length()!=16){
            throw new IncorrectCardNumberException();
        }
        if(p.getCvv().length()!=3)
        {
            throw new IncorrectCvvException();
        }
        System.out.println("Sto salvando il pagamento: "+p);
        Order newOrder = new Order();
        newOrder.setPayment(p);
        Order justAdded=orderRepository.save(newOrder);
        entityManager.refresh(justAdded);
        p.setOrder(justAdded);


        return paymentRepository.save(p);
    }

PaymentController:

@RestController
@RequestMapping("/payments")
public class PaymentController {
    @Autowired
    private PaymentService paymentService;

    @PostMapping("/createPayment")//funziona
    public ResponseEntity<Payment> create(@RequestBody @Valid Payment payment){
        System.out.print("Sono in paymentController.");
        try {
            Payment added=paymentService.addPayment(payment);
            return new ResponseEntity<>(added, HttpStatus.OK);
        } catch (PaymentAlreadyExistsException e) {
            return new ResponseEntity(new ResponseMessage("Payment already exists!"), HttpStatus.BAD_REQUEST);
        } catch (IncorrectCardNumberException e) {
            return new ResponseEntity(new ResponseMessage("Incorrect card number!"), HttpStatus.BAD_REQUEST);
        } catch (IncorrectCvvException e) {
            return new ResponseEntity(new ResponseMessage("Incorrect CVV"), HttpStatus.BAD_REQUEST);
        }
}
}

Payment entity:

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity
@Table(name = "payment", schema = "purchase")
public class Payment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private int id;

    @Basic
    @Column(name = "owner_name", nullable = true, length = 70)
    private String ownerName;

    @Basic
    @Column(name = "owner_last_name", nullable = true, length = 70)
    private String ownerLastName;

    @Basic
    @Column(name = "card_number", nullable = true, length = 16)
    private String cardNumber;

    @Basic
    @Temporal(TemporalType.DATE)

    @Column(name = "expiration", nullable = true)
    private Date expiration;

    @Basic
    @Column(name = "cvv", nullable = true, length = 3)
    private String cvv;

    @Basic
    @Column(name = "total", nullable = true)
    private float total;


    @OneToOne(mappedBy = "payment")
    private Order order;


}

Order entity:

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity

@Table(name = "order", schema = "purchase")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private int id;

    @Basic
    @CreationTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "order_time")
    private Date orderTime;

    @ManyToOne
    @JoinColumn(name = "buyer")
    private User buyer;



    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "payment_id")
    @JsonIgnore
    private Payment payment;

    @OneToOne
    @JoinColumn(name = "cart_id")
    private Cart cart;
}

I have the following method in PaymentService:

@Transactional(readOnly = true)//
    public List<Payment> getAllPayments(){
        List<Payment> result = paymentRepository.findAll();
        for(Payment p: result){
            if(p.getOrder()==null)
            System.out.println("Order in payment is null");
        }
        return paymentRepository.findAll();
    }

And when I execute it (after having created a Payment, an Order and tried to connect them using the createOrder method),

Order in payment is null

is printed, so the field order for that Payment is still null.

While, the Order oject is created in the following way in the database: enter image description here

I've edited createOrder method, as suggested, in the following way, but the problem persists:

@Transactional(readOnly = false)
    public Order createOrder (int paymentId, int userId, int cartId) throws PaymentDoesNotExistException{
        User user = userRepository.findById(userId);
        Payment payment = paymentRepository.findById(paymentId);
        if(payment == null)
        { throw new PaymentDoesNotExistException();
        }
        System.out.println("Payment: "+payment);
        Cart c = cartRepository.findById(cartId);
        Order o = new Order();
        o.setPayment(payment);
        o.setCart(c);
        o.setBuyer(user);
        payment.setOrder(o);
        orderRepository.save(o);
        return o;

    }

Solution

  • I saw couple of unwanted things. No need to call refresh. Just create child objects and save them first and then save the parent. Just a guidance here, make your own custom exceptions and messages.

        Payment payment = paymentRepository.findById(paymentId);
        if(payment == null)
        { throw Exception("Payment not found");}
        System.out.println("Payment: "+payment);
        Cart c = cartRepository.findById(cartId);
        Order o = new Order();
        o.setPayment(payment);
        o.setCart(c);
        o.setBuyer(user);
        orderRepository.save(o);