avatarGeorge Sotiropoulos

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

11209

Abstract

he OrderItem.</i></p><p id="f719"><b>In addition we are using the productId and not the OrderItem id to search through the list of OrderItems.</b></p><p id="1c14"><b>OrderItem.Java</b></p><div id="ac57"><pre><span class="hljs-keyword">package</span> com.platform.ecommerce.order.model;

<span class="hljs-keyword">import</span> io.quarkus.hibernate.orm.panache.PanacheEntity; <span class="hljs-keyword">import</span> jakarta.persistence.*;

<span class="hljs-meta">@Entity</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderItem</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">PanacheEntity</span> {

<span class="hljs-comment">//default constructor</span>
<span class="hljs-keyword">public</span> <span class="hljs-title function_">OrderItem</span><span class="hljs-params">()</span> {
}


<span class="hljs-keyword">public</span> <span class="hljs-title function_">OrderItem</span><span class="hljs-params">(Order order, Product product, Integer quantity)</span> {
    <span class="hljs-built_in">this</span>.order = order;
    <span class="hljs-built_in">this</span>.product = product;
    <span class="hljs-built_in">this</span>.quantity = quantity;
    totalAmount = product.price*quantity;
}

<span class="hljs-keyword">public</span> Long <span class="hljs-title function_">getId</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> id;
}


<span class="hljs-meta">@Column(nullable = false)</span>
<span class="hljs-keyword">public</span> <span class="hljs-type">double</span> totalAmount;

<span class="hljs-meta">@Column(nullable = false)</span>
<span class="hljs-keyword">public</span> Integer quantity;

<span class="hljs-meta">@Transient</span>
<span class="hljs-keyword">public</span> Long productId;

<span class="hljs-meta">@OneToOne</span>
<span class="hljs-meta">@JoinColumn(name = "product_id",referencedColumnName = "id",insertable = true,updatable = false)</span>
<span class="hljs-keyword">public</span> <span class="hljs-type">Product</span> <span class="hljs-variable">product</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Product</span>();


<span class="hljs-meta">@ManyToOne(optional = false, fetch = FetchType.EAGER)</span>
<span class="hljs-meta">@JoinColumn(name="order_id", nullable=false)</span>
<span class="hljs-meta">@JsonIgnore</span>
<span class="hljs-keyword">public</span> Order order;


<span class="hljs-meta">@Transient</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span>  <span class="hljs-title function_">setTotalPrice</span><span class="hljs-params">()</span> {
    totalAmount = product.price*quantity;
}

} </pre></div><h1 id="ca24">Step 2. Create Resource Endpoint for Orders.</h1><p id="2df8"><b>OrderResource.java</b></p><div id="1de9"><pre><span class="hljs-keyword">package</span> com.platform.ecommerce.order.resource;

<span class="hljs-keyword">import</span> com.platform.ecommerce.order.model.OrderItem; <span class="hljs-keyword">import</span> com.platform.ecommerce.order.model.Order; <span class="hljs-keyword">import</span> io.quarkus.logging.Log; <span class="hljs-keyword">import</span> jakarta.enterprise.context.ApplicationScoped; <span class="hljs-keyword">import</span> jakarta.inject.Inject; <span class="hljs-keyword">import</span> jakarta.ws.rs.*; <span class="hljs-keyword">import</span> jakarta.ws.rs.core.MediaType; <span class="hljs-keyword">import</span> jakarta.ws.rs.core.Response;

<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-meta">@Path("order-service/v1/order")</span> <span class="hljs-meta">@ApplicationScoped</span> <span class="hljs-meta">@Produces(MediaType.APPLICATION_JSON)</span> <span class="hljs-meta">@Consumes(MediaType.APPLICATION_JSON)</span>

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderResource</span> {

<span class="hljs-meta">@GET</span>
<span class="hljs-meta">@Path("all")</span>
<span class="hljs-keyword">public</span> List&lt;Order&gt; <span class="hljs-title function_">getAllOrders</span><span class="hljs-params">()</span> {<span class="hljs-keyword">return</span> Order.listAll(); }

<span class="hljs-meta">@GET</span>
<span class="hljs-meta">@Path("{id}")</span>
<span class="hljs-keyword">public</span> Order <span class="hljs-title function_">findById</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("id")</span> Long id)</span> {
     <span class="hljs-type">Order</span> <span class="hljs-variable">order</span> <span class="hljs-operator">=</span> Order.findById(id);
     <span class="hljs-keyword">return</span> order;
}

<span class="hljs-meta">@POST</span>
<span class="hljs-meta">@Path("create/{customerId}")</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">createOrder</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("customerId")</span> Long customerId, Order order)</span> {
    Order.createOrder(customerId,order);
    Log.info(<span class="hljs-string">"Order created:"</span>+order.id);
    <span class="hljs-keyword">return</span> Response.status(Response.Status.CREATED).build();
}
<span class="hljs-meta">@PUT</span>
<span class="hljs-meta">@Path("update/status/{id}")</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">updateOrderStatus</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("id")</span> Long id, String status)</span> {
    Order.updateOrderStatus(id,status);
    <span class="hljs-keyword">return</span> Response.status(Response.Status.NO_CONTENT).build();
}
<span class="hljs-meta">@Path("add/item/{orderId}")</span>
<span class="hljs-meta">@POST</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">addOrderItem</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("orderId")</span> Long orderId, OrderItem orderItemForm)</span> {
   <span class="hljs-type">Product</span> <span class="hljs-variable">product</span> <span class="hljs-operator">=</span> Product.findProductById(orderItemForm.productId);
   Order.addOrderItem(orderId, product, orderItemForm.quantity);
   <span class="hljs-keyword">return</span> Response.status(Response.Status.OK).build();
       
}

<span class="hljs-meta">@Path("update/item/{orderId}")</span>
<span class="hljs-meta">@PUT</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">updateOrderItemQuantity</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("orderId")</span> Long orderId, OrderItem orderItemForm)</span> {
        Order.updateOrderItemQuantity(orderId,orderItemForm.productId,orderItemForm.quantity);
        <span class="hljs-keyword">return</span> Response.status(Response.Status.OK).build();
}

<span class="hljs-meta">@Path("delete/item/{orderId}")</span>
<span class="hljs-meta">@PUT</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">deleteOrderItem</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("orderId")</span> Long orderId, OrderItem orderItemForm)</span> {
    Order.deleteOrderItem(orderId,orderItemForm.productId);
    <span class="hljs-keyword">return</span> Response.status(Response.Status.OK).build();
}

<span class="hljs-meta">@GET</span>
<span class="hljs-meta">@Path("customer/{id}")</span>
<span class="hljs-keyword">public</span>  List&lt;Order&gt; <span class="hljs-title function_">findOrdersByCustomerId</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("id")</span> Long id)</span> {
    <span class="hljs-keyword">return</span> Order.findOrdersByCustomerId(id);
}

<span class="hljs-meta">@DELETE</span>
<span class="hljs-meta">@Path("delete/{id}")</span>
<span class="hljs-keyword">public</span> Response <span class="hljs-title function_">deleteOrder</span><span class="hljs-params">(<span class="hljs-meta">@PathParam("id")</span> Long id)</span> {
    Order.deleteOrder(id);
    <span class="hljs-keyword">return</span> Response.status(Response.Status.OK).build();
}

}</pre></div><h1 id="2d00">Step 3. Add Sample Data for Customers.</h1><p id="b36e">To load SQL statements when Hibernate ORM starts, add an <code>import.sql</code> file to the root of your <code>resources</code> directory. This script can contain any SQL DML statements. Make sure to terminate each statement with a semicolon.(Source:<a href="https://quarkus.io/guides/hibernate-orm">https://quarkus.io/guides/hibernate-orm</a>).</p><p id="a624">We have already added some custom data for products in the first part. Now its time to add a couple of customer records.</p><p id="a457"><b>import.sql</b></p><div id="7bd9"><pre><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> customer (id, name, surname,email,address,phone) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'customer_seq'</span>), <span class="hljs-string">'Marco'</span>,<span class="hljs-string">'Verratti'</span>,<span class="hljs-string">'[email protected]'</span>,<span class="hljs-string">'Princes Park (Le Parc des Princes)'</span>,<span class="hljs-string">'000000000'</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> customer (id, name, surname,email,address,phone) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'customer_seq'</span>), <span class="hljs-string">'Kylian'</span>,<span class="hljs-string">'Mbappé'</span>,<span class="hljs-string">'kylian.mbappé@gmail.com'</span>,<span class="hljs-string">'Princes Park (Le Parc des Princes)'</span>,<span class="hljs-string">'000000000'</span>);

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacMini M1'</span>,<span class="hljs-number">699.00</span>,<span class="hljs-number">0</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacMini M2'</span>,<span class="hljs-number">899.00</span>,<span class="hljs-number">5</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacBook Air M1'</span>,<span class="hljs-number">999.00</span>,<span class="hl

Options

js-number">100</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacBook Air M2'</span>,<span class="hljs-number">1399.00</span>,<span class="hljs-number">100</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacStudio M2 Max'</span>,<span class="hljs-number">2390.00</span>,<span class="hljs-number">100</span>); <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> product (id, name, price,stock) <span class="hljs-keyword">VALUES</span> ( nextval(<span class="hljs-string">'product_seq'</span>), <span class="hljs-string">'MacStudio M2 Ultra'</span>,<span class="hljs-number">4399.00</span>,<span class="hljs-number">100</span>);</pre></div><h1 id="2e91">Step 4. Create Testing for our Orders Endpoint.</h1><p id="72cb">Its time to write our Test for our OrderResource. I’m using the @Order annotation to specify the order of execution. For this test we create 1 order, then we add 1 order item of product with id:1 and quantuty 2. Then we update the quantity to 40 and test again. Finally we delete the order item and test if we are fetching an order with an empty list of order items.</p><div id="dcdf"><pre><span class="hljs-keyword">package</span> com.platform.ecommerce.order.resource;

<span class="hljs-keyword">import</span> com.platform.ecommerce.order.model.OrderStatus; <span class="hljs-keyword">import</span> com.platform.ecommerce.order.model.OrderItem; <span class="hljs-keyword">import</span> io.quarkus.test.common.http.TestHTTPEndpoint; <span class="hljs-keyword">import</span> io.quarkus.test.junit.QuarkusTest; <span class="hljs-keyword">import</span> jakarta.json.Json; <span class="hljs-keyword">import</span> jakarta.json.JsonObject; <span class="hljs-keyword">import</span> jakarta.ws.rs.core.MediaType; <span class="hljs-keyword">import</span> org.junit.jupiter.api.*; <span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> io.restassured.RestAssured.given;

<span class="hljs-meta">@QuarkusTest</span> <span class="hljs-meta">@TestHTTPEndpoint(OrderResource.class)</span> <span class="hljs-meta">@TestMethodOrder(MethodOrderer.OrderAnnotation.class)</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderResourceTest</span> {

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Order(1)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">createOrder</span><span class="hljs-params">()</span> {

    <span class="hljs-type">JsonObject</span> <span class="hljs-variable">order</span> <span class="hljs-operator">=</span> Json.createObjectBuilder()
            .add(<span class="hljs-string">"status"</span>, OrderStatus.Pending).build();


    <span class="hljs-comment">// Test POST</span>
    given()
            .contentType(MediaType.APPLICATION_JSON)
            .body(order.toString())
            .when()
            .when().post(<span class="hljs-string">"create/{customerId}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">201</span>);
}


<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Order(2)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addOrderItem</span><span class="hljs-params">()</span> {

    <span class="hljs-type">JsonObject</span> <span class="hljs-variable">orderItem</span> <span class="hljs-operator">=</span> Json.createObjectBuilder()
            .add(<span class="hljs-string">"productId"</span>, <span class="hljs-number">1</span>)
            .add(<span class="hljs-string">"quantity"</span>, <span class="hljs-number">2</span>)
            .build();


    <span class="hljs-comment">// Test POST</span>
    given()
            .contentType(MediaType.APPLICATION_JSON)
            .body(orderItem.toString())
            .when()
            .when().post(<span class="hljs-string">"add/item/{orderId}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>);
}


<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Order(3)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">whenFetchOrder_ThenAtLeastOneOrderShouldBeFound</span><span class="hljs-params">()</span> {
    <span class="hljs-comment">//TEST GET</span>
    com.platform.ecommerce.order.model.<span class="hljs-type">Order</span> <span class="hljs-variable">order</span> <span class="hljs-operator">=</span> given()
            .accept(MediaType.APPLICATION_JSON)
            .when().get(<span class="hljs-string">"{id}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>)
            .extract()
            .body().as(com.platform.ecommerce.order.model.Order.class);

}


<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Order(4)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">whenUpdateOrderItemQuantity_ThenQuantityShouldBeEqual</span><span class="hljs-params">()</span> {
    <span class="hljs-type">JsonObject</span> <span class="hljs-variable">orderItemJSON</span> <span class="hljs-operator">=</span> Json.createObjectBuilder()
            .add(<span class="hljs-string">"productId"</span>, <span class="hljs-number">1</span>)
            .add(<span class="hljs-string">"quantity"</span>, <span class="hljs-number">40</span>)
            .build();

    <span class="hljs-comment">// Test UPDATE</span>
    given()
            .contentType(MediaType.APPLICATION_JSON)
            .body(orderItemJSON.toString())
            .when().put(<span class="hljs-string">"update/item/{orderId}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>);

    <span class="hljs-comment">//FETCH ORDER</span>
    com.platform.ecommerce.order.model.<span class="hljs-type">Order</span> <span class="hljs-variable">order</span> <span class="hljs-operator">=</span> given()
            .accept(MediaType.APPLICATION_JSON)
            .when().get(<span class="hljs-string">"{id}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>)
            .extract()
            .body().as(com.platform.ecommerce.order.model.Order.class);


    <span class="hljs-comment">//EXTRACT THE ORDER ITEM FOR PRODUCT WITH ID=1</span>
    <span class="hljs-type">OrderItem</span> <span class="hljs-variable">orderItem</span> <span class="hljs-operator">=</span> order.orderItems.stream().filter(o -&gt; o.product.id.intValue()==<span class="hljs-number">1</span>).findFirst().orElse(<span class="hljs-literal">null</span>);
    Assertions.assertEquals(orderItem.quantity,<span class="hljs-number">40</span>);

}


<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Order(4)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">whenDeleteOrderItem_ThenSizeOfItemsShouldBeZero</span><span class="hljs-params">()</span> {
    <span class="hljs-type">JsonObject</span> <span class="hljs-variable">orderItemJSON</span> <span class="hljs-operator">=</span> Json.createObjectBuilder()
            .add(<span class="hljs-string">"productId"</span>, <span class="hljs-number">1</span>)
            .build();

    <span class="hljs-comment">// Test UPDATE</span>
    given()
            .contentType(MediaType.APPLICATION_JSON)
            .body(orderItemJSON.toString())
            .when().put(<span class="hljs-string">"delete/item/{orderId}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>);

    <span class="hljs-comment">//FETCH ORDER</span>
    com.platform.ecommerce.order.model.<span class="hljs-type">Order</span> <span class="hljs-variable">order</span> <span class="hljs-operator">=</span> given()
            .accept(MediaType.APPLICATION_JSON)
            .when().get(<span class="hljs-string">"{id}"</span>,<span class="hljs-number">1</span>)
            .then()
            .statusCode(<span class="hljs-number">200</span>)
            .extract()
            .body().as(com.platform.ecommerce.order.model.Order.class);


    <span class="hljs-comment">//EXTRACT THE ORDER ITEM FOR PRODUCT WITH ID=1</span>
    Assertions.assertEquals(order.orderItems.size(),<span class="hljs-number">0</span>);

}

}

</pre></div><p id="4edb">Last step is to open up a terminal and run the test by typing</p><div id="1bd4"><pre>mvn clean <span class="hljs-built_in">test</span></pre></div><p id="d7ec">Run the program by typing <b>mvn quarkus:dev</b> if you are using maven or if you are using quarkus cli type <b>quarkus dev</b></p><div id="6839"><pre>mvn quarkus:dev

OR

quarkus dev</pre></div><p id="d196">That’s all!</p><p id="c409">Our system is a basic one and there is lots of functionality to be added.</p><p id="887f" type="7">its a monolithic application. Also all the ‘product’ functionality is handled by this application, although it should be handled in a separate micro-service one.</p><p id="8679" type="7">For the purpose of the tutorial I did that on-purpose, and in the articles to come will we break our application into 2 different ones, into 2 different micro services.</p><p id="0f65">In addition in the next articles we will explore how we can add security, how to check items from the inventory, configuration profiles, kubernetes deployments and much more.</p><p id="71a0">Thank you for reading this article!</p><p id="b4eb">TUTORIAL PARTS</p><p id="3339"><a href="https://readmedium.com/part-1-crud-with-panache-orm-in-quarkus-an-eshop-example-cd37ccf051e2">Part 1: Quarkus Persistence, CRUD with Panache. E-commerce example.</a></p><p id="b10d"><a href="https://readmedium.com/part-2-quarkus-persistence-crud-with-panache-e-commerce-example-a19aa5af13d9">Part 2: Quarkus Persistence, CRUD with Panache. E-commerce example.</a></p><p id="c67b"><a href="https://readmedium.com/part-3-1-quarkus-e1b8f9732a5f">Part 3.1: Quarkus. From Monolithic to a MicroService Architecture. Branch By Abstraction. REST Client Reactive. E-commerce example.</a></p></article></body>

Part 2: Quarkus Persistence, CRUD with Panache. E-commerce example.

In the first part of this tutorial we developed the entity for the products using the Active Record Patttern. In this tutorial we are creating the rest of the entities needed below

Customer

Order

OrderItem

The relations are:

OneToMany: one customer has many orders

OneToMany: one order has many order items

OneToOne: one order item has one product

Step 1. Entity classes

Customer.Java

package com.platform.ecommerce.order.model;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;

import java.util.ArrayList;
import java.util.List;


@Entity
public class Customer extends PanacheEntity{


    @Column(nullable = false)
    public  String name;

    @Column(nullable = false)
    public  String surname;

    @Column(nullable = false)
    public  String email;

    @Column(nullable = false)
    public  String address;
    @Column(nullable = false)
    public  String phone;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnore
    public List<Order> orderList = new ArrayList<>();

}

Order.Java

package com.platform.ecommerce.order.model;


import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.logging.Log;
import io.quarkus.panache.common.Sort;
import jakarta.persistence.*;
import jakarta.transaction.Transactional;
import org.hibernate.annotations.CreationTimestamp;
import jakarta.persistence.Column;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "orders")
public class Order extends PanacheEntity {


    @Column(nullable = false)
    public double totalAmount;

    @Column(nullable = false)
    public String status;


    @CreationTimestamp
    @Column(updatable = false, nullable = false)
    public ZonedDateTime created;

    @ManyToOne()
    @JoinColumn(name = "customer_id", nullable = false)
    public Customer customer;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    public List<OrderItem> orderItems = new ArrayList<>();


    @Transient
    public void setTotalPrice() {
        double total = 0D;
        for (OrderItem item : orderItems) {
            total += item.totalAmount;
        }
        totalAmount = total;
    }


    public static Order findOrderById(Long id) {
        return findById(id);
    }

    public static List<Order> findOrdersByStatus(String status) {
        List<Order> orderList = Order.list("status", Sort.by("customer.id").and("created"), status);
        return orderList;
    }

    public static List<Order> findOrdersByCustomerId(Long customerId) {
        List<Order> orderList = Order.list("customer.id", Sort.by("created"), customerId);
        return orderList;
    }

    @Transactional
    public static void createOrder(Long customerId, Order order) {
        Customer customer = Customer.findById(customerId);
        order.customer = customer;
        order.persist();
    }

    @Transactional
    public static void addOrderItem(Long orderId, Product product, Integer quantity) {
        Order order =  findOrderById(orderId);
        OrderItem orderItem = new OrderItem(order,product,quantity);
        order.orderItems.add(orderItem);
        order.setTotalPrice();
        order.persist();
    }



    @Transactional
    public static void updateOrderItemQuantity(Long orderId, Long productId, Integer quantity) {
        Order order =  findOrderById(orderId);
        Log.info("Order found:"+order.id);

        OrderItem orderItem = order.orderItems.stream().filter(o -> productId.equals(o.product.id)).findFirst().orElse(null);
        Log.info("Order item found:"+orderItem.id);

        orderItem.quantity = quantity;
        Log.info("Update quantity to:"+quantity);

        orderItem.setTotalPrice();
        order.setTotalPrice();
        order.persist();
    }

    @Transactional
    public static void updateOrderStatus(Long id, String status) {
        Order order = findOrderById(id);
        order.status = status;
        order.persist();
    }

    @Transactional
    public static void deleteOrderItem(Long orderId, Long productId) {
        Order order = findOrderById(orderId);
        Log.info("Order found:"+order.id);

        OrderItem orderItem = order.orderItems.stream().filter(o -> productId.equals(o.product.id)).findFirst().orElse(null);
        Log.info("Order item found:"+orderItem.id);

        order.orderItems.remove(orderItem);
        Log.info("Remove item:"+orderItem.id);

        order.setTotalPrice();
        order.persist();
    }


    @Transactional
    public static void deleteOrder(Long id) {
        findById(id).delete();
    }


}

Tip:Notice how the functionality of adding order items and removing order items is done within the addOrderItem and removeOrderItem functions of the orders class. Since we are using CascadeType.ALL at the @OneToMany relationship any change to the list of orderItems, which is a change in the parent entity will be propagated to the child class which in this case is the OrderItem.

In addition we are using the productId and not the OrderItem id to search through the list of OrderItems.

OrderItem.Java

package com.platform.ecommerce.order.model;


import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;



@Entity
public class OrderItem extends PanacheEntity {


    //default constructor
    public OrderItem() {
    }


    public OrderItem(Order order, Product product, Integer quantity) {
        this.order = order;
        this.product = product;
        this.quantity = quantity;
        totalAmount = product.price*quantity;
    }

    public Long getId() {
        return id;
    }


    @Column(nullable = false)
    public double totalAmount;
    
    @Column(nullable = false)
    public Integer quantity;

    @Transient
    public Long productId;

    @OneToOne
    @JoinColumn(name = "product_id",referencedColumnName = "id",insertable = true,updatable = false)
    public Product product = new Product();


    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name="order_id", nullable=false)
    @JsonIgnore
    public Order order;


    @Transient
    public void  setTotalPrice() {
        totalAmount = product.price*quantity;
    }



}

Step 2. Create Resource Endpoint for Orders.

OrderResource.java

package com.platform.ecommerce.order.resource;


import com.platform.ecommerce.order.model.OrderItem;
import com.platform.ecommerce.order.model.Order;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;


import java.util.List;

@Path("order-service/v1/order")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)

public class OrderResource {

    @GET
    @Path("all")
    public List<Order> getAllOrders() {return Order.listAll(); }

    @GET
    @Path("{id}")
    public Order findById(@PathParam("id") Long id) {
         Order order = Order.findById(id);
         return order;
    }

    @POST
    @Path("create/{customerId}")
    public Response createOrder(@PathParam("customerId") Long customerId, Order order) {
        Order.createOrder(customerId,order);
        Log.info("Order created:"+order.id);
        return Response.status(Response.Status.CREATED).build();
    }
    @PUT
    @Path("update/status/{id}")
    public Response updateOrderStatus(@PathParam("id") Long id, String status) {
        Order.updateOrderStatus(id,status);
        return Response.status(Response.Status.NO_CONTENT).build();
    }
    @Path("add/item/{orderId}")
    @POST
    public Response addOrderItem(@PathParam("orderId") Long orderId, OrderItem orderItemForm) {
       Product product = Product.findProductById(orderItemForm.productId);
       Order.addOrderItem(orderId, product, orderItemForm.quantity);
       return Response.status(Response.Status.OK).build();
           
    }

    @Path("update/item/{orderId}")
    @PUT
    public Response updateOrderItemQuantity(@PathParam("orderId") Long orderId, OrderItem orderItemForm) {
            Order.updateOrderItemQuantity(orderId,orderItemForm.productId,orderItemForm.quantity);
            return Response.status(Response.Status.OK).build();
    }

    @Path("delete/item/{orderId}")
    @PUT
    public Response deleteOrderItem(@PathParam("orderId") Long orderId, OrderItem orderItemForm) {
        Order.deleteOrderItem(orderId,orderItemForm.productId);
        return Response.status(Response.Status.OK).build();
    }

    @GET
    @Path("customer/{id}")
    public  List<Order> findOrdersByCustomerId(@PathParam("id") Long id) {
        return Order.findOrdersByCustomerId(id);
    }

    @DELETE
    @Path("delete/{id}")
    public Response deleteOrder(@PathParam("id") Long id) {
        Order.deleteOrder(id);
        return Response.status(Response.Status.OK).build();
    }

}

Step 3. Add Sample Data for Customers.

To load SQL statements when Hibernate ORM starts, add an import.sql file to the root of your resources directory. This script can contain any SQL DML statements. Make sure to terminate each statement with a semicolon.(Source:https://quarkus.io/guides/hibernate-orm).

We have already added some custom data for products in the first part. Now its time to add a couple of customer records.

import.sql

INSERT INTO customer (id, name, surname,email,address,phone) VALUES ( nextval('customer_seq'), 'Marco','Verratti','[email protected]','Princes Park (Le Parc des Princes)','000000000');
INSERT INTO customer (id, name, surname,email,address,phone) VALUES ( nextval('customer_seq'), 'Kylian','Mbappé','kylian.mbappé@gmail.com','Princes Park (Le Parc des Princes)','000000000');

INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacMini M1',699.00,0);
INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacMini M2',899.00,5);
INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacBook Air M1',999.00,100);
INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacBook Air M2',1399.00,100);
INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacStudio M2 Max',2390.00,100);
INSERT INTO product (id, name, price,stock) VALUES ( nextval('product_seq'), 'MacStudio M2 Ultra',4399.00,100);

Step 4. Create Testing for our Orders Endpoint.

Its time to write our Test for our OrderResource. I’m using the @Order annotation to specify the order of execution. For this test we create 1 order, then we add 1 order item of product with id:1 and quantuty 2. Then we update the quantity to 40 and test again. Finally we delete the order item and test if we are fetching an order with an empty list of order items.

package com.platform.ecommerce.order.resource;

import com.platform.ecommerce.order.model.OrderStatus;
import com.platform.ecommerce.order.model.OrderItem;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.ws.rs.core.MediaType;
import org.junit.jupiter.api.*;
import static io.restassured.RestAssured.given;

@QuarkusTest
@TestHTTPEndpoint(OrderResource.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderResourceTest {


    @Test
    @Order(1)
    public void createOrder() {

        JsonObject order = Json.createObjectBuilder()
                .add("status", OrderStatus.Pending).build();


        // Test POST
        given()
                .contentType(MediaType.APPLICATION_JSON)
                .body(order.toString())
                .when()
                .when().post("create/{customerId}",1)
                .then()
                .statusCode(201);
    }


    @Test
    @Order(2)
    public void addOrderItem() {

        JsonObject orderItem = Json.createObjectBuilder()
                .add("productId", 1)
                .add("quantity", 2)
                .build();


        // Test POST
        given()
                .contentType(MediaType.APPLICATION_JSON)
                .body(orderItem.toString())
                .when()
                .when().post("add/item/{orderId}",1)
                .then()
                .statusCode(200);
    }


    @Test
    @Order(3)
    public void whenFetchOrder_ThenAtLeastOneOrderShouldBeFound() {
        //TEST GET
        com.platform.ecommerce.order.model.Order order = given()
                .accept(MediaType.APPLICATION_JSON)
                .when().get("{id}",1)
                .then()
                .statusCode(200)
                .extract()
                .body().as(com.platform.ecommerce.order.model.Order.class);

    }


    @Test
    @Order(4)
    public void whenUpdateOrderItemQuantity_ThenQuantityShouldBeEqual() {
        JsonObject orderItemJSON = Json.createObjectBuilder()
                .add("productId", 1)
                .add("quantity", 40)
                .build();

        // Test UPDATE
        given()
                .contentType(MediaType.APPLICATION_JSON)
                .body(orderItemJSON.toString())
                .when().put("update/item/{orderId}",1)
                .then()
                .statusCode(200);

        //FETCH ORDER
        com.platform.ecommerce.order.model.Order order = given()
                .accept(MediaType.APPLICATION_JSON)
                .when().get("{id}",1)
                .then()
                .statusCode(200)
                .extract()
                .body().as(com.platform.ecommerce.order.model.Order.class);


        //EXTRACT THE ORDER ITEM FOR PRODUCT WITH ID=1
        OrderItem orderItem = order.orderItems.stream().filter(o -> o.product.id.intValue()==1).findFirst().orElse(null);
        Assertions.assertEquals(orderItem.quantity,40);

    }


    @Test
    @Order(4)
    public void whenDeleteOrderItem_ThenSizeOfItemsShouldBeZero() {
        JsonObject orderItemJSON = Json.createObjectBuilder()
                .add("productId", 1)
                .build();

        // Test UPDATE
        given()
                .contentType(MediaType.APPLICATION_JSON)
                .body(orderItemJSON.toString())
                .when().put("delete/item/{orderId}",1)
                .then()
                .statusCode(200);

        //FETCH ORDER
        com.platform.ecommerce.order.model.Order order = given()
                .accept(MediaType.APPLICATION_JSON)
                .when().get("{id}",1)
                .then()
                .statusCode(200)
                .extract()
                .body().as(com.platform.ecommerce.order.model.Order.class);


        //EXTRACT THE ORDER ITEM FOR PRODUCT WITH ID=1
        Assertions.assertEquals(order.orderItems.size(),0);

    }

}

Last step is to open up a terminal and run the test by typing

mvn clean test

Run the program by typing mvn quarkus:dev if you are using maven or if you are using quarkus cli type quarkus dev

mvn quarkus:dev

OR 

quarkus dev

That’s all!

Our system is a basic one and there is lots of functionality to be added.

its a monolithic application. Also all the ‘product’ functionality is handled by this application, although it should be handled in a separate micro-service one.

For the purpose of the tutorial I did that on-purpose, and in the articles to come will we break our application into 2 different ones, into 2 different micro services.

In addition in the next articles we will explore how we can add security, how to check items from the inventory, configuration profiles, kubernetes deployments and much more.

Thank you for reading this article!

TUTORIAL PARTS

Part 1: Quarkus Persistence, CRUD with Panache. E-commerce example.

Part 2: Quarkus Persistence, CRUD with Panache. E-commerce example.

Part 3.1: Quarkus. From Monolithic to a MicroService Architecture. Branch By Abstraction. REST Client Reactive. E-commerce example.

Quarkus
Java
Quarkus Framework
Backend Development
Hibernate
Recommended from ReadMedium