Let’s add the Place Order feature to our E-commerce app using Spring Boot
In this tutorial, we will learn how to create an order and display the order history of the user
Introduction
We are building an e-commerce app from scratch using Vue.js in the frontend and Java with Spring boot for the backend. You can check out the first front-end tutorial of this series here.

Let’s Develop an E-Commerce Application From Scratch Using Java and Spring | by Nil Madhab | Javarevisited | Medium
Nil Madhab ・ ・
Medium
Placing the order is an important feature for an e-commerce app that allows users to order the products of their choice.
We will first develop the backend API using Spring Boot and in the next tutorial, we will use this API for the frontend part of our e-commerce website.
Refer the tutorial where we implemented the checkout feature here for the backend part
Live Demo
You can test the API at the following swagger link. You will find the cart API in order-controllersection(Run the code in your local first)
You can find the complete code at Github.
Pre-requisites
Knowledge of Java, OOP & Spring Boot Framework
Java Development Kit (JDK)
IntelliJ IDEA Ultimate — open-source (Recommended)
MySQL/MariaDB Database
A good browser (Chrome — recommended)
Project Structure
Preview of the API we will be building
placeOrder(/order/add) : This is a post method that is responsible for saving the order of the user.
getAllOrders(/order/): This is a get method that returns the list of all the orders of a particular user. Therefore it requires only the token of the user as a parameter.
The flow of the tutorial
Table/Model design
Create Repository
Implement Dto
Implement Service class (Business logic)
Implement Controller class (Building APIs)
Table Design
Model
Before we begin to code, we must invest some time thinking about the model/table design. So we require two tables which will be linked with each other. So in the existing modelpackage, create two files Order.java and OrderItem.java .
The first table/model (we name it as order)will contain attributes such as id(primary key), user_id(foreign key), created_date, session-id (the payment session-id we generated), and the total_price .
User id we will be fetching from the users table.
If you are wondering about the session id, please refer to this tutorial where we have explained the checkout session.
The second table/model (we name it as orderitems) will actually contain the products that are present in a particular order. There we need to link these two tables. So the primary key i.e. idfrom the order table will be used as a foreign key in the orderitems table. So the attributes of the orderitems table will be order_item_id, created_date, price, product_id(foreign key), quantity, order_id(foreign key).
Product id we will be obtained from the products table.
Order model
Table relationship
order table and orderitems table has a relation type of one to many, i.e. one row(entry) in order table is related to multiple rows(items of that particular order) in the orderitems table.
Similarly, order and user tables have a relation type of many to one i.e. multiple orders (entries/rows) are related to a single user (row/entry), because a user may have multiple orders.
@Entity | |
@Table(name="order") | |
public class Order { | |
@Id | |
@GeneratedValue(strategy = GenerationType.IDENTITY) | |
private Integer id; | |
@Column(name = "user_id") | |
private @NotBlank Integer userId; | |
@Column(name = "created_date") | |
private Date createdDate; | |
@Column(name = "total_price") | |
private Double totalPrice; | |
@Column(name = "session-id") | |
private String sessionId; | |
@OneToMany(cascade = CascadeType.ALL,orphanRemoval = true) | |
@JoinColumn(name = "order_id",referencedColumnName = "id",insertable = false,updatable = false) | |
private List<OrderItem> orderItems; | |
@ManyToOne(cascade = CascadeType.ALL) | |
@JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false) | |
private User user; | |
public Order() { | |
} | |
public Order(PlaceOrderDto orderDto, int userId, String sessionId){ | |
this.userId = userId; | |
this.createdDate = new Date(); | |
this.totalPrice = orderDto.getTotalPrice(); | |
this.sessionId = sessionId; | |
} | |
//getter and setter are removed for sake of brevity | |
} |
Order items model
Table relationship
The ordertable we created just now and this orderitems table has a relation type of many to one since multiple items (products) can be in relation with a particular order.
products table and the orderitems table has a relation type of one to one since a row(item) in the orderitems table is related to only one row(item) in the products table.
@Entity | |
@Table(name = "orderitems") | |
public class OrderItem { | |
@Id | |
@GeneratedValue(strategy = GenerationType.IDENTITY) | |
private Integer orderItemId; | |
@Column(name = "productId") | |
private @NotNull Long productId; | |
@Column(name = "quantity") | |
private @NotNull int quantity; | |
@Column(name = "price") | |
private @NotNull double price; | |
@Column(name = "order_id") | |
private Integer orderId; | |
@Column(name = "created_date") | |
private Date createdDate; | |
@ManyToOne | |
@JoinColumn(name = "order_id",referencedColumnName = "id",insertable = false,updatable = false) | |
private Order order; | |
@OneToOne | |
@JoinColumn(name = "productId",referencedColumnName = "id",insertable = false,updatable = false) | |
private Product product; | |
public OrderItem(){} | |
public OrderItem(Integer orderId, @NotNull Long product_id, @NotNull int quantity, @NotNull double price) { | |
this.productId = product_id; | |
this.quantity = quantity; | |
this.price = price; | |
this.orderId=orderId; | |
this.createdDate = new Date(); | |
} | |
//getter and setter are removed for sake of brevity | |
} |
Repository
Create two java files in the existing repository package OrderRespository.java and OrderItemsRespository.java .
- Firstly we create repository interface for the order table(OrderRespository.java).
Creating CRUD methods manually means writing a lot of boilerplate code unless you let the JPARepository interface carry about routine implementations for you. So, we will extend the JPARepository and create the interface CartRepository.
Extending JPARepository will automatically create and implement methods for the basic CRUD operations.
-
We will define a method findAllByUserIdOrderByCreatedDateDesc to fetch the orders list of a user and then order the list by created the date of each entry in the order table. The implementation of this method will be managed automatically by the JPARepository .
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters@Repository public interface OrderRepository extends JpaRepository<Order, Integer> { List<Order> findAllByUserIdOrderByCreatedDateDesc(Integer userId); } -
Similarly we create repository interface for the orderitems table(OrderItemsRespository.java).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characterspackage com.webtutsplus.ecommerce.repository; import com.webtutsplus.ecommerce.model.OrderItem; import org.springframework.data.jpa.repository.JpaRepository; public interface OrderItemsRepository extends JpaRepository<OrderItem,Integer> { }
DTO
In the dto package create a package named order and in which create 3 files orderDto.java , OrderItemsDto.java and placeOrderDto.java .
-
We create OrderDto for as a dummy object for the Order model with two fields id and userId .
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characterspublic class OrderDto { private Integer id; private @NotNull Integer userId; public OrderDto(Order order) { this.setId(order.getId()); this.setUserId(order.getUserId()); } //getter and setter are removed for sake of brevity } - Similarly we create OrderItemsDto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
public class OrderItemsDto { private @NotNull double price; private @NotNull int quantity; private @NotNull int orderId; private @NotNull int productId; public OrderItemsDto () {} public OrderItemsDto(@NotNull double price, @NotNull int quantity, @NotNull int orderId, @NotNull int productId) { this.price = price; this.quantity = quantity; this.orderId = orderId; this.productId = productId; } //getter and setter are removed for sake of brevity } - Now in the PlaceOrderDto we declare three fields id, userId, and the totalCostof the order. This dto will be used to save the order in the order table.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
public class PlaceOrderDto { private Integer id; private @NotNull Integer userId; private @NotNull Double totalPrice; public PlaceOrderDto() { } public PlaceOrderDto(Order order) { this.setId(order.getId()); this.setUserId(order.getUserId()); this.setTotalPrice(order.getTotalPrice()); } //getter and setter are removed for sake of brevity }
- Similarly we create OrderItemsDto
Now, let’s implement the Service class to interact with the order and orderitems table. In the OrderRepositoryinterface, we defined the methods to interact with the database.
In the Service class, we will call these methods and implement the so-called business logic. We will create methods for adding the order, fetching all orders of a user. Inside these methods, we will call the methods defined in the OrderRepositoryinterface.
- First, let’s implement the orderitems service class. In this class, we implement one method in order to save an item/product in a particular order.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
@Service @Transactional public class OrderItemsService { @Autowired private OrderItemsRepository orderItemsRepository; public void addOrderedProducts(OrderItem orderItem) { orderItemsRepository.save(orderItem); } } - Now let’s create the order service class which will contain the logic for placing the order and fetching the order details.
placeOrder: This method is used to set the PlaceOrderDto and it receives the id of the order from the saveOrder method. This id is returned from the saveOrder method because we need this id as an attribute in the orderItems table. Also using the object of cartService class we fetch the items in the cart and then save those in the orderitems table using the method in the orderItemsService class. And once the order is placed we delete the cart i.e. remove the items in the cart.
@Service | |
public class OrderService { | |
@Autowired | |
private OrderRepository orderRepository; | |
@Autowired | |
private CartService cartService; | |
@Autowired | |
OrderItemsService orderItemsService; | |
public void placeOrder(int userId, String sessionId) { | |
CartDto cartDto = cartService.listCartItems(userId); | |
PlaceOrderDto placeOrderDto = new PlaceOrderDto(); | |
placeOrderDto.setUserId(userId); | |
placeOrderDto.setTotalPrice(cartDto.getTotalCost()); | |
int orderId = saveOrder(placeOrderDto, userId, sessionId); | |
List<CartItemDto> cartItemDtoList = cartDto.getcartItems(); | |
for (CartItemDto cartItemDto : cartItemDtoList) { | |
OrderItem orderItem = new OrderItem( | |
orderId, | |
cartItemDto.getProduct().getId(), | |
cartItemDto.getQuantity(), | |
cartItemDto.getProduct().getPrice()); | |
orderItemsService.addOrderedProducts(orderItem); | |
} | |
cartService.deleteCartItems(userId); | |
} | |
public int saveOrder(PlaceOrderDto orderDto, int userId, String sessionID){ | |
Order order = getOrderFromDto(orderDto,userId,sessionID); | |
return orderRepository.save(order).getId(); | |
} | |
private Order getOrderFromDto(PlaceOrderDto orderDto, int userId,String sessionID) { | |
Order order = new Order(orderDto,userId,sessionID); | |
return order; | |
} | |
public List<Order> listOrders(int user_id) { | |
List<Order> orderList = orderRepository.findAllByUserIdOrderByCreatedDateDesc(user_id); | |
return orderList; | |
} | |
} |
saveOrder: This method first creates the order object and then using the method of orderRepository it saves the order in the order table and returns the id which we used in the previous method.
listOrders : This method will take the user id as a parameter and then return a list of all the orders from the order table corresponding to that user id.
Controller
Here we will build our APIs.
In the controller package creates a file OrderController.java and annotate the class with RestController annotation. In this OrderController class, we will use the methods we defined in the orderService class.
- So firstly we will implement the API for saving the order. Create a method placeOrder that accepts token and the session Id as parameters.
This method which throws an exception
if the invalid user(not logged in) user tries to place the order
if the user (valid) tries to place the order of the product which does not exist in the products table.
@PostMapping("/add") | |
public ResponseEntity<ApiResponse> placeOrder(@RequestParam("token") String token, @RequestParam("sessionId") String sessionId) | |
throws ProductNotExistException, AuthenticationFailException { | |
authenticationService.authenticate(token); | |
int userId = authenticationService.getUser(token).getId(); | |
orderService.placeOrder(userId, sessionId); | |
return new ResponseEntity<ApiResponse>(new ApiResponse(true, "Order has been placed"), HttpStatus.CREATED); | |
} |
- Since we added the order to the table, now we build an API to fetch the orders of the user. This method throws an exception if an invalid user tries to fetch the order history.
@GetMapping("/") | |
public ResponseEntity<List<Order>> getAllOrders(@RequestParam("token") String token) throws AuthenticationFailException { | |
authenticationService.authenticate(token); | |
int userId = authenticationService.getUser(token).getId(); | |
List<Order> orderDtoList = orderService.listOrders(userId); | |
return new ResponseEntity<List<Order>>(orderDtoList,HttpStatus.OK); | |
} |
Congratulations!!!
We have now successfully added the order feature to our e-commerce app using Spring Boot. In the next tutorial we will implement the front end part using these APIs using Vuejs, so stay tuned!
Top comments (0)