Learn to create apis/methods for crud operations in spring boot application which modify the data in database using hibernate/jpa persistence apis.
1. Overview
In this example, we are creating crud operations and exposing them through REST APIs so that UI clients can invoke these operations. The demo operations enable the clients to modify the employee records in database.
The purpose of this demo is to showcase the nuts and bolts, which make this interaction possible, not covering the complexity in business logic involved in real world applications.
2. Maven Dependencies
In this example, we are using maven to add runtime jars in project. If you are using gradle then please find related dependencies.
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.1.5.RELEASE</ version > < relativePath /> <!-- lookup parent from repository --> </ parent > < groupId >com.howtodoinjava.demo</ groupId > < artifactId >SpringBoot2Demo</ artifactId > < version >0.0.1-SNAPSHOT</ version > < name >SpringBoot2Demo</ name > < description >Demo project for Spring Boot</ description > < properties > < java.version >1.8</ java.version > </ properties > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-jpa</ artifactId > </ dependency > < dependency > < groupId >com.h2database</ groupId > < artifactId >h2</ artifactId > < scope >runtime</ scope > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build > </ project > |
- spring-boot-starter-web : It is used for building web layer, including REST APIs, applications using Spring MVC. Uses Tomcat as the default embedded container.
- spring-boot-starter-data-jpa : It includes spring data, hibernate, HikariCP, JPA API, JPA Implementation (default is hibernate), JDBC and other required libraries.
- h2 : Though we can add any database easily using datasource properties in
application.properties
file, we are using h2 database in reduce unnecessacery complexity. - spring-boot-starter-test : It is used to test Spring Boot applications with libraries including JUnit, Hamcrest and Mockito.
3. Hibernate Configuration
3.1. Entity and repository
The first step to work with data in database is to model it’s structure in JPA entity classes and create repository interfaces for them.
Whenever possible, extend JpaRepository
interface to allows to create repository implementations automatically, at runtime, for any given entity class. The types of entity class and it’s ID field are specified in the generic parameters on JpaRepository
.
Remember to include only JPA API annotations (javax.persistence.*
) to de-couple hibernate from application code.
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table (name= "TBL_EMPLOYEES" ) public class EmployeeEntity { @Id @GeneratedValue private Long id; @Column (name= "first_name" ) private String firstName; @Column (name= "last_name" ) private String lastName; @Column (name= "email" , nullable= false , length= 200 ) private String email; //Setters and getters @Override public String toString() { return "EmployeeEntity [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]" ; } } |
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.howtodoinjava.demo.entity.EmployeeEntity; @Repository public interface EmployeeRepository extends JpaRepository<EmployeeEntity, Long> { } |
3.2. Datasource Configuration
To connect to database, we must configure the datasource. We are using H2 database so respective properties are used.
Also, we have used couple of more properties to enable H2 console and extensive logging.
spring.datasource.url=jdbc:h2:file:~/test spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect # Enabling H2 Console spring.h2.console.enabled= true # Custom H2 Console URL spring.h2.console.path=/h2-console # create database schema from SQL files spring.jpa.hibernate.ddl-auto=none #Turn Statistics on and log SQL stmts spring.jpa.show-sql= true spring.jpa.properties.hibernate.format_sql= true spring.jpa.properties.hibernate.generate_statistics= false #logging.level.org.hibernate.type=trace #logging.level.org.hibernate.stat=debug logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n |
4. Service (uses repository)
The service layer is optional – still recommended to perform additional business logic if any. Generally, we will connect with repository here for crud operations.
import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.howtodoinjava.demo.entity.EmployeeEntity; import com.howtodoinjava.demo.exception.RecordNotFoundException; import com.howtodoinjava.demo.repository.EmployeeRepository; @Service public class EmployeeService { @Autowired EmployeeRepository repository; public List<EmployeeEntity> getAllEmployees() { List<EmployeeEntity> employeeList = repository.findAll(); if (employeeList.size() > 0 ) { return employeeList; } else { return new ArrayList<EmployeeEntity>(); } } public EmployeeEntity getEmployeeById(Long id) throws RecordNotFoundException { Optional<EmployeeEntity> employee = repository.findById(id); if (employee.isPresent()) { return employee.get(); } else { throw new RecordNotFoundException( "No employee record exist for given id" ); } } public EmployeeEntity createOrUpdateEmployee(EmployeeEntity entity) throws RecordNotFoundException { Optional<EmployeeEntity> employee = repository.findById(entity.getId()); if (employee.isPresent()) { EmployeeEntity newEntity = employee.get(); newEntity.setEmail(entity.getEmail()); newEntity.setFirstName(entity.getFirstName()); newEntity.setLastName(entity.getLastName()); newEntity = repository.save(newEntity); return newEntity; } else { entity = repository.save(entity); return entity; } } public void deleteEmployeeById(Long id) throws RecordNotFoundException { Optional<EmployeeEntity> employee = repository.findById(id); if (employee.isPresent()) { repository.deleteById(id); } else { throw new RecordNotFoundException( "No employee record exist for given id" ); } } } |
5. REST Controller
Finally expose all operations through MVC URLs or REST endpoints. Clients will connect with these endpoints to get/update/delete employees records.
Notice the usage of annotations @RestController, @RequestMapping, @GetMapping, @PostMapping and @DeleteMapping to map various URIs to controller methods.
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.howtodoinjava.demo.entity.EmployeeEntity; import com.howtodoinjava.demo.exception.RecordNotFoundException; import com.howtodoinjava.demo.service.EmployeeService; @RestController @RequestMapping ( "/employees" ) public class EmployeeController { @Autowired EmployeeService service; @GetMapping public ResponseEntity<List<EmployeeEntity>> getAllEmployees() { List<EmployeeEntity> list = service.getAllEmployees(); return new ResponseEntity<List<EmployeeEntity>>(list, new HttpHeaders(), HttpStatus.OK); } @GetMapping ( "/{id}" ) public ResponseEntity<EmployeeEntity> getEmployeeById( @PathVariable ( "id" ) Long id) throws RecordNotFoundException { EmployeeEntity entity = service.getEmployeeById(id); return new ResponseEntity<EmployeeEntity>(entity, new HttpHeaders(), HttpStatus.OK); } @PostMapping public ResponseEntity<EmployeeEntity> createOrUpdateEmployee(EmployeeEntity employee) throws RecordNotFoundException { EmployeeEntity updated = service.createOrUpdateEmployee(employee); return new ResponseEntity<EmployeeEntity>(updated, new HttpHeaders(), HttpStatus.OK); } @DeleteMapping ( "/{id}" ) public HttpStatus deleteEmployeeById( @PathVariable ( "id" ) Long id) throws RecordNotFoundException { service.deleteEmployeeById(id); return HttpStatus.FORBIDDEN; } } |
6. Spring boot crud operations demo
Now when coding part is done, start the spring boot application. It will live all URL endpoints along with H2 database console.
HTTP GET http://localhost:8080/employees
Console Hibernate:
select
employeeen0_.id as id1_0_,
employeeen0_.email as email2_0_,
employeeen0_.first_name as first_na3_0_,
employeeen0_.last_name as last_nam4_0_
from
tbl_employees employeeen0_
API Response [
{
"id"
:
1
,
"firstName"
:
"Lokesh"
,
"lastName"
:
"Gupta"
,
"email"
:
"abc@gmail.com"
},
{
"id"
:
2
,
"firstName"
:
"Deja"
,
"lastName"
:
"Vu"
,
"email"
:
"xyz@email.com"
},
{
"id"
:
3
,
"firstName"
:
"Caption"
,
"lastName"
:
"America"
,
"email"
:
"cap@marvel.com"
}
]
HTTP GET http://localhost:8080/employees/2
Console Hibernate:
select
employeeen0_.id as id1_0_0_,
employeeen0_.email as email2_0_0_,
employeeen0_.first_name as first_na3_0_0_,
employeeen0_.last_name as last_nam4_0_0_
from
tbl_employees employeeen0_
where
employeeen0_.id=?
API Response {
"id"
:
2
,
"firstName"
:
"Deja"
,
"lastName"
:
"Vu"
,
"email"
:
"xyz@email.com"
}
Drop me your questions in comments related to creating and exposing CRUD operations in spring boot applications having JPA hibernate to manage backend data updates.
No comments:
Post a Comment