Introduction To Spring Data JPA With Inheritance in A REST Application
Spring is a platform designed to simplify the life of Java developers. This tutorial will delve into one of Spring Data JPA's key features, inheritance.
Join the DZone community and get the full member experience.
Join For FreeSpring is a platform that has revolutionized how Java developers build applications, providing a robust framework and a wide range of tools to simplify the development process. One of the standout features of Spring is Spring Data JPA, which offers seamless integration between Java Persistence API (JPA) and the Spring Framework, making it easier to work with databases in a Java application.
In this tutorial, we will delve into a critical aspect of Spring Data JPA: inheritance. Inheritance is vital in object-oriented programming, allowing us to create hierarchies of classes that share common attributes and behaviors. Managing legacy can sometimes pose challenges when persisting these objects in a database.
Spring Data JPA provides an elegant solution to this challenge by supporting various inheritance strategies. Whether using single-table inheritance, joined-table inheritance, or table-per-class inheritance, Spring Data JPA provides the tools and abstractions to map and persist your object hierarchy in a database seamlessly.
This article will focus specifically on incorporating inheritance in a RESTful application using Spring Data JPA. We will explore how to define and map entity classes that utilize inheritance and how to perform CRUD (Create, Read, Update, Delete) operations on these entities using the powerful features of Spring Data JPA.
In the second part, we will cover the basic annotations on Spring Core and the Spring Web, focusing on Rest Controller; however, we did not create an application with a database connection.
Fortunately, this part's goal is to make a connection easier and clean. We'll use Spring Data because it reduces a lot of code and uses the JPA annotations.
To explore the JPA annotations with a repository, we'll create simple rest to handle a person and their tickets through plane, train, and bus. The foremost goal here is to explore the heritage of both the JPA and JSON serialization.
It is essential to highlight that there is an impedance mismatch. Thus objects are not RDBMS, and if we don't think about it will apply to performance and a bad design.
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotBlank;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class Person {
strategy = GenerationType.AUTO) (
private Long id;
message = "the name is mandatory in person") (
private String name;
private String city;
fetch = FetchType.EAGER, cascade = CascadeType.ALL) (
private List<Ticket> tickets;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getCity() {
return city;
}
public List<Ticket> getTickets() {
if(Objects.isNull(tickets)) {
return Collections.emptyList();
}
return tickets;
}
void update(Person person) {
this.city = person.city;
this.name = person.name;
}
//...
}
Only to show the heritage, there is a Ticket as an abstract class with some specialization. We used the Ticket class as an entity with a single table strategy, and in Jackson, the values of the subtype both are using the field type.
xxxxxxxxxx
strategy = InheritanceType.SINGLE_TABLE) (
name = "type", discriminatorType = DiscriminatorType.STRING) (
use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") (
Type(value = BoatTicket.class, name = "BOAT"), ({ .
Type(value = BusTicket.class, name = "BUS"), .
Type(value = PlaneTicket.class, name = "PLANE") .
})
public abstract class Ticket {
strategy = GenerationType.AUTO) (
private Long id;
private BigDecimal value;
public BigDecimal getValue() {
return value;
}
public abstract TicketType getType();
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Ticket ticket = (Ticket) o;
return Objects.equals(id, ticket.id);
}
public int hashCode() {
return Objects.hashCode(id);
}
public String toString() {
return "Ticket{" +
"id=" + id +
", value=" + value +
'}';
}
}
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
"PLANE") (
public class PlaneTicket extends Ticket {
public TicketType getType() {
return TicketType.PLAIN;
}
}
"BUS") (
public class BusTicket extends Ticket {
public TicketType getType() {
return TicketType.BUS;
}
}
"BOAT") (
public class BoatTicket extends Ticket {
public TicketType getType() {
return TicketType.BOAT;
}
}
The repository class integrates the database smoothly because the developer does not need to handle the implementation but with an interface. This repository has several features, such as query by method, and uses the Query annotation to execute queries without handling the undertaking.
xxxxxxxxxx
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
interface PersonRepository extends JpaRepository<Person, Long> {
}
The last layer is the controller, where we'll do a tight integration with the repository once it is a light application. The PersonController has pagination features beyond the CRUD operation.
xxxxxxxxxx
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.util.stream.Stream;
"users") (
public class PersonController {
private static final int SIZE = 10;
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
public Stream<Person> findAll( (value = "page", defaultValue = "0") int page) {
Page<Person> people = this.repository.findAll(PageRequest.of(page, SIZE, Sort.by("name", "id")));
return people.get();
}
"{id}") (
public Person findById( Long id) {
return this.repository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
public Person insert( Person person) {
return this.repository.save(person);
}
"{id}") (
public Person update( Long id, Person person) {
Person personDatabase = this.repository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
personDatabase.update(person);
return this.repository.save(personDatabase);
}
"{id}") (
public void delete( Long id) {
this.repository.deleteById(id);
}
}
In this tutorial, we explored the Spring Data JPA API basics using the inheritance feature on both JPA and JSON. Spring has several resources that allow us to create a REST application with a database integration with a couple of classes. It is essential to say that it is a first step in the JPA and Spring world, where there are references to go deep below.
References
Opinions expressed by DZone contributors are their own.
Comments