Spring boot REST API with JPA
Objectives
The main goal is to create small application which could evolve to microservice. The application gather data from Oracle database then expose that data by REST service with JSON objects. There were used a few additional libraries:- HikariCP - additional library to manage connection pool to database
- Swagger - libraries to create documentation in OpenAPI standard
- Lombok - library to reduce boilerplate code in java classes
Below is the structure of application:
The application's code
The model
Based on Oracle database image which was created in my previous post (https://java-architect.blogspot.com/2020/05/oracle-plsql-part-1.html) I build selected java Entities. Lombok library was very helpful to significantly reduce boilerplate java code. It was necessary to use annotation "@JsonManagedReference" and "@JsonBackReference" for protecting against getting an errors.
The Region class:
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "REGIONS")
public class Region {
@Id
@Column(name="REGION_ID")
private Long regionId;
@NotNull
@Column(name = "REGION_NAME")
private String name;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "region")
@JsonManagedReference
private List<Country> countries;
public Region(){
}
}
The Country class:
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "COUNTRIES")
public class Country {
@Id
@Column(name="COUNTRY_ID")
private String countryId;
@Column(name = "COUNTRY_NAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REGION_ID", nullable = false)
@JsonBackReference
private Region region;
public Country() {
}
}
The repository
@Repository
public interface RegionRepository extends CrudRepository<Region, Long> { Optional<Region> findById(Long id);
}
@Repository
public interface CountryRepository extends CrudRepository<Country, String>{
Optional<Country> findById(String countryId);
@Query("select c from Country c where c.name = :name")
Stream<Country> findByNameReturnStream(@Param("name") String name);
}
The service
@Transactional
@Service("regionService")
public class RegionServiceImpl implements RegionService{
@Autowired
CountryRepository countryRepository;
@Autowired
RegionRepository regionRepository;
public List<RegionDTO> getRegions() {
List<RegionDTO> list = new ArrayList<RegionDTO>();
for(Region r : regionRepository.findAll()) {
list.add(new RegionDTO(r));
}
return list;
}
public RegionDTO getRegionByIdv1(Long id) {
return new RegionDTO(regionRepository.findById(id).get());
}
public RegionDTOv2 getRegionByIdv2(Long id) {
Region region = regionRepository.findById(id).get();
region.getCountries();
return new RegionDTOv2(region);
}
}
The RestController
@RestController
@EnableSwagger2
public class BaseController {
@Autowired
@Qualifier("regionService")
RegionService regionService;
@RequestMapping(method = RequestMethod.GET, value = "/regions")
public ResponseEntity<List<RegionDTO>> regions() {
return new ResponseEntity<List<RegionDTO>>(regionService.getRegions(), HttpStatus.OK);
}
@RequestMapping(value = "/regions/simple/{regionId}", method = RequestMethod.GET)
public ResponseEntity<RegionDTO> getRegionById(@PathVariable("regionId") long regionId) {
RegionDTO regionDTO = regionService.getRegionByIdv1(regionId);
return new ResponseEntity<RegionDTO>(regionDTO, HttpStatus.OK);
}
@RequestMapping(value = "/regions/complex/{regionId}", method = RequestMethod.GET)
public ResponseEntity<RegionDTOv2> getRegionByIdV2(@PathVariable("regionId") long regionId) {
RegionDTOv2 regionDTOv2 = regionService.getRegionByIdv2(regionId);
return new ResponseEntity<RegionDTOv2>(regionDTOv2, HttpStatus.OK);
}
}
Application.properties
#spring boot configuration for Oracle
spring.datasource.url=jdbc:oracle:thin:@localhost:49161:xe
spring.datasource.username=hr
spring.datasource.password=hr
spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver
#hibernate dialect config
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
spring.jackson.serialization.fail-on-empty-beans=false
spring.jpa.show-sql=true
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
# HikariCP settings
spring.datasource.hikari.*
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.maximum-pool-size=10
The results
Specification
The results can be visualized in web browser. Let's see the API's documentation.The output of method "/regions"
The base output of method "/regions" has:JSON representation:
Data representation:
And the Headers:
The output of method "/regions/complex/{regionId}"
What else
The CRUD operations are usually mapped to HTTP method in REST web services like below:
| |