Home > Software design >  Stats in entity response DTO
Stats in entity response DTO

Time:12-16

I need to return a DTO response with the entity's statistics, I can return the statistics using an interface, like this:

Current response:

    [
        {
            "name": "Customer 1",
            "id": 1,
            "countOrdersPending": 1,
            "countOrdersCompleted": 2,
            "countOrders": 3,
            "totalAmount": 950.0
        },
        {
            "name": "Customer 2",
            "id": 2,
            "countOrdersPending": 2,
            "countOrdersCompleted": 3,
            "countOrders": 5,
            "totalAmount": 1867.5
        }
    ]

I return statistics attributes, but I need to return a customerStats object like this:

Expected response:

    [
        {        
            "id": 1,
            "name": "Customer 1",
            "customerStats": {
                "countOrdersPending": 1,
                "countOrdersCompleted": 2,
                "countOrders": 3,
                "totalAmount": 950.0
            }
            
        },
        {        
            "id": 2,
            "name": "Customer 2",
            "customerStats": {
                "countOrdersPending": 2,
                "countOrdersCompleted": 3,
                "countOrders": 5,
                "totalAmount": 1867.5
            }
        }
    ]

Can someone give me a tip on how to implement this?

Here are the classes:

Customer Entity

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "customers")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

}

Order Entity

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "orders")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false, foreignKey = @ForeignKey(name = "fk_order_customer1"))
    private Customer customer;
    private Double amount;

    @Enumerated(value = EnumType.STRING)
    private OrderStatusType orderStatus;

}

OrderStatusType Enum

public enum OrderStatusType {
    PENDING,
    COMPLETED
}

CustomerStatsResponse Interface

public interface CustomerStatsResponse {
    Long getId();
    String getName();
    Double getTotalAmount();
    Long getCountOrders();
    Long getCountOrdersPending();
    Long getCountOrdersCompleted();
}

Customer Repository

import com.example.demo.dto.CustomerStatsResponse;
import com.example.demo.model.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface CustomerRep extends JpaRepository<Customer, Long> {

    @Query(value = "SELECT c.id, c.name, "  
            "SUM(o.amount) AS totalAmount, "  
            "COUNT(o.id) AS countOrders, "  
            "(SELECT COUNT(ord.id) FROM orders ord "  
            "WHERE ord.customer_id = c.id AND ord.order_status = 'PENDING') as countOrdersPending, "  
            "(SELECT COUNT(ord.id) FROM orders ord "  
            "WHERE ord.customer_id = c.id AND ord.order_status = 'COMPLETED') as countOrdersCompleted "  
            "FROM customers c "  
            "INNER JOIN orders o ON c.id = o.customer_id "  
            "GROUP BY c.id, c.name"
            , nativeQuery = true)
    List<CustomerStatsResponse> customerStats();

}

Order Repository

import com.example.demo.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRep extends JpaRepository<Order, Long> {
}

Customer Service

import com.example.demo.dto.CustomerStatsResponse;
import com.example.demo.model.Customer;
import com.example.demo.repository.CustomerRep;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class CustomerService {

    private final CustomerRep customerRep;

    public List<Customer> findAll() {
        return customerRep.findAll();
    }

    public List<CustomerStatsResponse> customerStats() {
        return customerRep.customerStats();
    }
}

Order Service

import com.example.demo.model.Order;
import com.example.demo.repository.OrderRep;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRep orderRep;

    public List<Order> findAll() {
        return orderRep.findAll();
    }
}

Customer Controller

import com.example.demo.dto.CustomerStatsResponse;
import com.example.demo.model.Customer;
import com.example.demo.service.CustomerService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("api/v1/customers")
@RequiredArgsConstructor
public class CustomerController {

    private final CustomerService customerService;

    @GetMapping
    public ResponseEntity<List<Customer>> findAll() {
        return ResponseEntity.ok(customerService.findAll());
    }

    @GetMapping("/stats")
    public ResponseEntity<List<CustomerStatsResponse>> customerStats() {
        return ResponseEntity.ok(customerService.customerStats());
    }
}

Order Controller

import com.example.demo.model.Order;
import com.example.demo.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

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

    private final OrderService orderService;

    @GetMapping
    public ResponseEntity<List<Order>> findAll() {
        return ResponseEntity.ok(orderService.findAll());
    }
}

CodePudding user response:

Just create a CustomerStatsResponseDto that match your wanted JSON and that accepts a CustomerStatsResponse in its constructor:

public class CustomerStatsResponseDto {
    private Long id;
    private String name;
    private CustomerDto customerStats;

    public CustomerStatsResponseDto(CustomerStatsResponse customerStatsResponse) {
        this.id = customerStatsResponse.getId();
        this.name = customerStatsResponse.getName();
        this.customerStats = new CustomerStatsDto(customerStatsResponse.getTotalAmount(), customerStatsResponse.getCountOrders(),
          customerStatsResponse.getCountOrdersPending(), customerStatsResponse.getCountOrdersCompleted());
    }
}
public class CustomerStatsDto {
    private Double totalAmount;
    private Long countOrders;
    private Long countOrdersPending;
    private Long countOrdersCompleted;

    public CustomerStatsDto(Double totalAmount, Long countOrders, Long countOrdersPending, Long countOrdersCompleted) {
        this.totalAmount = totalAmount;
        this.countOrders = countOrders;
        this.countOrdersPending = countOrdersPending;
        this.countOrdersCompleted = countOrdersCompleted;
    }
}

Now in your CustomerController you just need to convert from CustomerStatsResponse to CustomerStatsResponseDto as follows:

@RestController
@RequestMapping("api/v1/customers")
@RequiredArgsConstructor
public class CustomerController {

    private final CustomerService customerService;

    @GetMapping
    public ResponseEntity<List<Customer>> findAll() {
        return ResponseEntity.ok(customerService.findAll());
    }

    @GetMapping("/stats")
    public ResponseEntity<List<CustomerStatsResponseDto>> customerStats() {
        return ResponseEntity.ok(customerService.customerStats().stream()
                .map(customerStatsResponse -> new CustomerStatsResponseDto(customerStatsResponse))
                .collect(Collectors.toList()));
    }
}
  • Related