Spring Data JPA many to one mapping & Integration test

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

Leave a Reply

Your email address will not be published. Required fields are marked *