The key goal of Spring Data repository abstraction is to significantly reduce the amount of code required to implement data access layers for various persistence stores. In this example I will show you how to create a JPA repository to save and retrieve all Person
model. NOTE: If you need to know what tools you need to have installed in yout computer in order to create a Spring Boot basic project, please refer my previous post: Spring Boot
Then execute this command in your terminal.
spring init --dependencies=web,jpa,thymeleaf --language=java --build=gradle spring-boot-jpa
This is the build.gradle file
generated:
buildscript {
ext {
springBootVersion = '2.0.6.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.jos.dem.springboot.jpa'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Now add lombok and mysql-connector dependencies to your build.gradle
file:
compile("mysql:mysql-connector-java:5.1.34")
compileOnly('org.projectlombok:lombok')
Lombok is a great tool to avoid boilerplate code, for knowing more please go here. In this example, you store Person
objects, annotated as a JPA entity.
package com.jos.dem.springboot.jpa.model;
import static javax.persistence.GenerationType.AUTO;
import javax.persistence.Id;
import javax.persistence.Entity;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import lombok.Data;
@Data
@Entity
public class Person {
@Id
@GeneratedValue(strategy=AUTO)
private Long id;
@Column(nullable=false)
private String nickname;
@Column(unique=true, nullable=false)
private String email;
}
And here is our PersonRepository
, by extending JpaRepository
we get a bunch of generic CRUD
methods into our type that allows save, find all persons and so on. Even we can define specific queries such as findByNickname('josdem')
. Second, this will allow the Spring Data JPA repository infrastructure to scan the classpath for this interface and create a Spring bean for it.
package com.jos.dem.springboot.jpa.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.jos.dem.springboot.jpa.model.Person;
public interface PersonRepository extends JpaRepository<Person, Long>{
Person save(Person person);
List<Person> findAll();
}
Now, let’s take a look to our controller.
package com.jos.dem.springboot.jpa.controller;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import com.jos.dem.springboot.jpa.model.Person;
import com.jos.dem.springboot.jpa.command.Command;
import com.jos.dem.springboot.jpa.command.PersonCommand;
import com.jos.dem.springboot.jpa.repository.PersonRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Controller
@RequestMapping("persons/**")
public class PersonController {
@Autowired
private PersonRepository personRepository;
private Logger log = LoggerFactory.getLogger(this.getClass());
@RequestMapping(method=GET)
public ModelAndView getAll(){
log.info("Listing all persons");
ModelAndView modelAndView = new ModelAndView("persons/list");
List<Person> persons = personRepository.findAll();
modelAndView.addObject("persons", persons);
return modelAndView;
}
@RequestMapping(value="create", method=GET)
public ModelAndView create(){
log.info("Creating person");
ModelAndView modelAndView = new ModelAndView("persons/create");
Command personCommand = new PersonCommand();
modelAndView.addObject("personCommand", personCommand);
return modelAndView;
}
@RequestMapping(method=POST)
public ModelAndView save(@Valid PersonCommand personCommand, BindingResult bindingResult){
log.info("Registering new Person: " + personCommand.getNickname());
ModelAndView modelAndView = new ModelAndView("persons/list");
if(bindingResult.hasErrors()){
modelAndView.setViewName("persons/create");
modelAndView.addObject("personCommand", personCommand);
return modelAndView;
}
Person person = new Person();
person.setNickname(personCommand.getNickname());
person.setEmail(personCommand.getEmail());
personRepository.save(person);
List<Person> persons = personRepository.findAll();
modelAndView.addObject("persons", persons);
return modelAndView;
}
}
It is important to notice that we are using a PersonCommand
to receive the person’s requested information from a client.
package com.jos.dem.springboot.jpa.command;
import javax.validation.constraints.Size;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Email;
import lombok.Data;
@Data
public class PersonCommand implements Command {
@NotNull
@Size(min=3, max=50)
private String nickname;
@Email
@NotNull
@Size(min=1, max=250)
private String email;
}
That’s it, we are using javax.validation
and org.hibernate.validator
to validate that the fields meets required constraints.
package com.jos.dem.springboot.jpa.command;
import java.io.Serializable;
public interface Command extends Serializable{}
Command
is just an interface that extends java.io.Serializable
<html>
<body>
<form id="create" th:action="@{/persons}" th:object="${personCommand}" method="post">
<label for="nickname">Nickname:</label>
<input type="text" name="nickname" th:field="*{nickname}" placeholder="nickname" id="nickname"/>
<label th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}"></label>
<br/>
<label for="email">Email:</label>
<input type="text" name="email" th:field="*{email}" placeholder="email" id="email"/>
<label th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></label>
<br/><br/>
<button id="btn-success" type="submit">Submit</button>
</form>
</body>
</html>
So if any error is detected in user’s information, we catch them using bindingResult.hasErrors()
method at the controller and so we are able to show them using <label th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}"></label>
label in a html.
Here is out application.properties
who is extenalized MySQL
database credentials.
spring.datasource.url=jdbc:mysql://localhost/spring_boot_jpa
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.generate-ddl=true
Finally you can go to this address: http://localhost:8080/persons/create and you will see our create person form. To browse the project go here, to download the project:
git clone https://github.com/josdem/spring-boot-jpa.git
git fetch
git checkout feature/jpa
To Run the project:
gradle bootRun