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"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <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
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_
[ { "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
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=?
{ "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.
Happy Learning !!
Hi, example shows application.properties with
spring.datasource.url=spring.datasource.url=jdbc:h2:file:~/test
But then I got an error, that table TBL_employees does not exisit.
It only works with
spring.datasource.url=jdbc:h2:file:C:/temp/test
Why?
Use spring.datasource.url=jdbc:h2:mem:testdb to resolve the issue
Hi team
in the controller part @RequestBody is missing for createOrUpdateEmployee method.
pls update it..
@RestController
annotation already includes@ResponseBody
annotation with it.Cannot load JDBC driver class ‘com.microsoft.sqlserver.jdbc.SQLServerDriver’ sir plzz give solution
hi, could you please provide how to upload images along with the above code.
I am also getting a 500 Internal Service Error related to the ID when making a new post through postman using JSON type. Can we get an example of a successful post request?
I am also unable to use post method due to the same error
Please add this code in EmployeeEntity class @GeneratedValue(strategy = GenerationType.IDENTITY) other it will throw sequence hibernate_sequence not found sql statement h2 exception
Simple and efficient app, thank you sir!
In the delete method, why do you return HttpStatus.FORBIDDEN? That would indicate the caller did not have permission to delete, even though the delete was successful.
i created new application and done whatever you have done with different form fields but getting 404 error response. i debug the application it is not going inside to controller methods.
I am getting the null value for one filed in my obj ..don’t know why
@RequestBody Solo debe agregase esta anotación para que el controller reciba los parámetros de entrada del Json
JPA insert work but update failed with spring boot 2.1.6
HHH000346: Error during managed flush [java.lang.StackOverflowError]
exception org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
this code is not working for “post” saying “status”:500,”error”:”Internal Server Error”,”message”:”The given id must not be null!;
Please use @RequestBody annotation which is missing in createOrUpdateEmployee method of controller:
Thanks , it worked
Hello, I think your request’s content-type is not understood by controller. I was trying to create a new EmployeeEntity using POST using ‘raw’ JSON – I received the above error. But then I switched to ‘form-data’ in Postman and added id, firstName, lastName, email in request body – and it started working.
Hi
Is my first time trying this I get this error Error: Could not find or load main class com.howtodoinjava.demo.DemoApplication
Thank,
Luis
Great, makes it simple and easy 🙂
Thank you
where is class RecordNotFoundException? where is this package com.howtodoinjava.demo.exception?
Please check the attached source code.