JavaSpringTechnology

Spring Boot with Spring Data JPA

Welcome to the Spring Boot with Spring Data JPA Tutorial. In this tutorial we are going to see how Spring Data JPA provides complete abstraction over the DAO layer. We don’t need to write the implementation for the DAO layer anymore Spring Data auto-generates the implementation DAO implementations. 

We already had an introduction to Spring Boot and for this tutorial we will use Spring Boot along with Spring Data JPA. In this tutorial, you will also see how Spring Boot auto-configuration helps getting datasource configurations done hassle free. 

In our tutorial Spring Boot Rest Service we had created a DogService which a simple CRUD service based on Mock Data Provider. In this tutorial we will use the same DogService and replace the Mock Data Provider with actual MySql Database along with Spring Data and JPA

1 Dependency Configuration

For this tutorial I am using MySQL database along with Spring Data-Jpa. Here is the build.gradle file

buildscript {
    ext {
        springBootVersion = '2.1.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.amitph.spring'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile('mysql:mysql-connector-java:8.0.13')

    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

2 Datasource Configuration

We now have dependencies configured. It is not time to tell which datasource to connect to. Here is my application.yml having spring boot datasource entries.

spring:
  datasource:
    url: jdbc:mysql://localhost:33099/dogs
    password: <ENTER _ PASSWORD _ HERE >
    username: root
    driver-class-name: "com.mysql.jdbc.Driver"
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: update

Here, we have specified jdbc url, username, password & driver class name (MySql).
Apart from this there are JPA specific configurations. First is database-platform which is to tell underlying hibernate to consider MySql query dialect. So that all the Database operations will be handled in MySql specific syntax. Second JPA configuration  is ddl-auto which is to tell hibernate, to create the respective database and table structure, if not already present.
When this option is turned on, hibernate will create the database structure based on the Entity Beans and datasource.

3 Entity Bean

First code level thing we will do is to write an Entity Bean. Here is what Oracle Documentation says about entity beans.

Using JPA, you can designate any POJO class as a JPA entity–a Java object whose nontransient fields should be persisted to a relational database using the services of an entity manager obtained from a JPA persistence provider (either within a Java EE EJB container or outside of an EJB container in a Java SE application).

In simpler words JPA Entity is any Java POJO which can represent the underlying table structure. As our service is based on Dog table we will create a Dog Entity object.

package com.amitph.spring.dogs.repo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Dog {
    @Id
    @GeneratedValue
    private long id;
    private String name;
    private int age;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

The above POJO is annotated with @Entity which is to denote this is an entity object for the table name Dog.
Then are are three fields, which represents the datable table columns. 
Field id is our Primary Key and hence marked as @Id

The field id is also marked with @GeneratedValue which denotes that this is an Auto Increment column and hibernate will take care of putting next value. Hibernate will first query the underlying table to know max value of the column and increment it with next insert. Which also means we don’t need to specify any value for Id column and can leave it blank. 

4 Repository Interface

The Repository represents DAO layer, which typically does all the database operations. Thanks to Spring data who provides the implementations for the methods. Let’s have a look at our DogsRepoisitory which extends CrudRepository

package com.amitph.spring.dogs.repo;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DogsRepository extends CrudRepository<Dog, Long> {}

There are no method declarations here in DogsRepository, that is because Spring Data’s CrudInterface has already declared basic CRUD methods.

Here, we are done with the JPA and Spring data things in other words the DAO layer. Let’s now write a simple Service Layer and a Controller. 

5 Controller and Service Layer

As we have our data access layer done, we will write our controller and service layer. Notice that the DogsRepository is annotated with @Repository which also adds it to the Spring Context. We can now Autowire the repository in Service.

5,1 Dogs Service

This class has simple CRUD methods. It also converts Entity bean to a Dto (data transfer object). DTO is also a simple java POJO, which is used to transfer data between systems. Here we are returning DTOs from our REST endpoints. 

package com.amitph.spring.dogs.service;

import com.amitph.spring.dogs.model.DogDto;
import com.amitph.spring.dogs.repo.Dog;
import com.amitph.spring.dogs.repo.DogsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;

@Component
public class DogsService {
    @Autowired DogsRepository repository;

    public void add(DogDto dto) {
        repository.save(toEntity(dto));
    }

    public void delete(long id) {
        repository.deleteById(id);
    }

    public List<Dog> getDogs() {
        return (List<Dog>) repository.findAll();
    }

    public Dog getDogById(long id) {
        Optional<Dog> optionalDog = repository.findById(id);
        return optionalDog.orElseThrow(() -> new DogNotFoundException("Couldn't find a Dog with id: " + id));
    }

    private Dog toEntity(DogDto dto) {
        Dog entity = new Dog();
        entity.setName(dto.getName());
        entity.setAge(dto.getAge());
        return entity;
    }
}

5.2 Dogs Controller

Dogs controller is a standard REST controller with simple CRUD endpoints. Job of the controller is to handle the Http requests and invoke Service class methods. 

package com.amitph.spring.dogs.web;

import com.amitph.spring.dogs.model.DogDto;
import com.amitph.spring.dogs.repo.Dog;
import com.amitph.spring.dogs.service.DogsService;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/dogs")
public class DogsController {
    @Autowired DogsService service;

    @GetMapping
    public List<Dog> getDogs() {
        return service.getDogs();
    }

    @PostMapping
    public void postDogs(@RequestBody DogDto dto) {
        service.add(dto);
    }

    @GetMapping("/{id}")
    public Dog getById(@PathVariable(required = true) long id) {
        return service.getDogById(id);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable(required = true) long id) {
        service.delete(id);
    }
}

Now the Dogs Service is ready to run. Start the Application and execute the HTTP endpoints and that’s it.

6 Conclusion

This is the end of Spring Boot with Spring data and JPA Tutorial we saw how to use Spring Data’s abstraction for the Data Access Layer. We saw, how to represent database table in the form of Entity Bean, how to Use Spring Data’s autogenerated Responsitory implementations. Additionally, we also saw how to use Spring Boot to do automatic datasource configurations.

In the Spring Boot Rest Service post we have already seen creating a RESTful web-service with Spring Boot. In the current article we did not care about Exception Handling. Visit Spring Rest Service Exception Handling to learn about handling exceptions. We also skipped the Unit Testing part here, which will be covered in upcoming articles. 


For full source code of the examples used here, please visit https://github.com/amitrp/dog-service-jpa.