In this blog we will see Spring Data JPA many to one relationship example and also how to write integration test for the same.
In Spring Data JPA, the “Many-to-One” relationship is used to establish an association between two entities where multiple instances of one entity are associated with a single instance of another entity.
To start with lets create the project with the shown dependencies
To describe the relationship create two entity classes, one representing the “many” side and the other representing the “one” side of the relationship.
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name="Orders")
@Builder
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int orderId;
int qty;
@ManyToOne
@JoinColumn(name = "productId")
private Product product;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int productId;
String productName;
}
In the Order
entity, the @ManyToOne
annotation is used to define the many-to-one relationship. The @JoinColumn
annotation specifies the column that will hold the foreign key relationship.
Define Spring Data JPA repository interfaces for both entities
@Repository
public interface OrdersRepository extends JpaRepository<Order, Integer>{
List<Order> findAllByProduct(Product product);
}
@Repository
public interface ProductsRepository extends JpaRepository<Product, Integer>{
}
To implement the entity relationship define below two API’s
i) add a product
ii) order a single product
Also define another API to get the total sales of a product
@RestController
public class ProductsalesController {
@Autowired
ProductsalesService service;
@Autowired
ProductsRepository prodRepo;
@PostMapping("/product")
public Product addProduct(@RequestBody Product product) {
return service.addProduct(product);
}
@PostMapping("/order/{productId}")
public Order createOrder(@PathVariable int productId, @RequestBody Order order) throws Exception {
Optional<Product> prod = prodRepo.findById(productId);
if (prod.isPresent()) {
Product product = prod.get();
order.setProduct(product);
return service.createOrder(order);
} else {
throw new ProductNotFoundException("Order failed.Product not found", HttpStatus.NOT_FOUND.value());
}
}
@GetMapping("/productsales/{productId}")
public int getSalesForProduct(@PathVariable int productId) {
return service.getSalesForProduct(productId);
}
@ExceptionHandler(ProductNotFoundException.class)
@ResponseStatus(code = HttpStatus.NOT_FOUND)
public ResponseEntity<String> handleProductNotFoundException(ProductNotFoundException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
}
}
Configure Spring Boot to use postgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/productsales
spring.datasource.username=${USERNAME}
spring.datasource.password=${PASSWORD}
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto = update
Service Implementation:
@Service
@NoArgsConstructor
@AllArgsConstructor
public class ProductsalesServiceImpl implements ProductsalesService{
@Autowired
ProductsRepository productRepo;
@Autowired
OrdersRepository orderRepo;
public Product addProduct(Product product) {
return productRepo.save(product);
}
@Override
public Order createOrder(Order order) {
return orderRepo.save(order);
}
@Override
public int getSalesForProduct(int productId) {
Optional<Product> prod = productRepo.findById(productId);
int totalSales =0;
List<Order> orders = orderRepo.findAllByProduct(prod.get());
for (Order order : orders) {
totalSales = totalSales+order.getQty();
}
return totalSales;
}
}
Testing the API using postman:
Testing Spring Boot controllers is an essential part of ensuring your application’s functionality and reliability. We will see a basic overview of how to write integration tests for Spring Boot controllers using the JUnit and Mockito frameworks.
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
@ActiveProfiles(value = "test")
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class ProductsalesControllerTest {
@Autowired
private TestRestTemplate restTemplate;
@MockBean
OrdersRepository orderRepo;
@MockBean
ProductsRepository productRepo;
@Autowired
ProductsalesService svc;
@Test
public void testGetSalesForProduct() throws URISyntaxException {
final String baseUrl = "/productsales/2";
Optional<Product> prod = Optional.ofNullable(Product.builder().productId(1).productName("mobile").build());
Order order1 = Order.builder().orderId(1).product(prod.get()).qty(5).build();
Order order2= Order.builder().orderId(2).product(prod.get()).qty(5).build();
List<Order> orders = new ArrayList<>();
orders.add(order1);
orders.add(order2);
when(productRepo.findById(any())).thenReturn(prod);
when(orderRepo.findAllByProduct(any())).thenReturn(orders);
ResponseEntity<Integer> result = this.restTemplate.getForEntity(baseUrl, Integer.class);
Assertions.assertEquals(200, result.getStatusCode().value());
Assertions.assertEquals(10, result.getBody().intValue());
}
}
For code please head to https://github.com/MMahendravarman/Springboot_Examples
In this blog using the Spring WebFlux module, we are going to leverage the functional…
Spring Cloud Function is a project within the Spring ecosystem that allows developers to build…
RabbitMQ is an open-source message broker software that implements the Advanced Message Queuing Protocol (AMQP).…
Spring Integration is a powerful extension of the Spring Framework designed to support the implementation…
The Spring Cloud Config Client is a component of the Spring Cloud framework that enables…
In Python, handling CSV (Comma Separated Values) files is easy using the built-in csv module.…