Thursday, December 31, 2020

Spring boot crud operations example with hibernate

 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.

pom.xml
<?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 APIJPA 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.

EmployeeEntity.java
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   + "]";
    }
}
EmployeeRepository.java
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.

application.properties
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.

EmployeeService.java
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.

EmployeeController.java
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

How to DROP SEQUENCE in Oracle?

  Oracle  DROP SEQUENCE   overview The  DROP SEQUENCE  the statement allows you to remove a sequence from the database. Here is the basic sy...