Spring Boot CrudRepository Example

By Arvind Rai, December 02, 2017
This page will walk through Spring Boot CrudRepository example. Spring Boot Data enables JPA repository support by default. CrudRepository provides generic CRUD operation on a repository for a specific type. CrudRepository is a Spring data interface and to use it we need to create our interface by extending CrudRepository. Spring provides CrudRepository implementation class automatically at runtime. It contains methods such as save, findById, delete, count etc. Spring boot automatically detects our repository if the package of that repository interface is the same or sub-package of the class annotated with @SpringBootApplication.
Spring Boot provides default database configurations when it scans Spring Data JPA in classpath. Spring boot uses spring-boot-starter-data-jpa starter to configure spring JPA. For data source we need to configure data source properties starting with spring.datasource.* in application.properties. In Spring Boot 2.0 release, default database pooling technology has been switched from Tomcat Pool to HikariCP. Spring boot prefers HikariCP on first place then Tomcat pooling and then Commons DBCP2 on the basis of availability. Here on this page we will create a Spring Boot Rest web Service for CRUD operation. CRUD operation will be performed by CrudRepository. Now find the complete example step by step.

1. Technologies Used

Find the technologies being used in our example.
1. Java 9
2. Spring 5.0.5.RELEASE
3. Spring Boot 2.0.1.RELEASE
4. Maven 3.5.2
5. MySQL 5.5
6. Eclipse Oxygen

2. Maven File used in Project

Find the pom.xml used in our example.
<?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>
	<groupId>com.concretepage</groupId>
	<artifactId>spring-boot-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>spring-demo</name>
	<description>Spring Boot Demo Project</description>
	<parent>
	    <groupId>org.springframework.boot</groupId>
  	    <artifactId>spring-boot-starter-parent</artifactId>
	    <version>2.0.1.RELEASE</version>
 	    <relativePath/>
	</parent>
	<properties>
	    <java.version>9</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>mysql</groupId>
		 <artifactId>mysql-connector-java</artifactId>
		 <version>6.0.5</version>
	  </dependency>	  	  	  
	  <dependency>
		 <groupId>javax.xml.bind</groupId>
		 <artifactId>jaxb-api</artifactId>
		 <version>2.3.0</version>
	  </dependency>  	      	  	  	  
          <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-devtools</artifactId>
                 <optional>true</optional>
          </dependency> 
	</dependencies>
	<build>
	  <plugins>
		 <plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		 </plugin>
	  </plugins>
	</build>
</project>  

3. CrudRepository Interface

CrudRepository is an interface and extends Spring data Repository interface. CrudRepository provides generic CRUD operation on a repository for a specific type. It has generic methods for CRUD operation. To use CrudRepository we have to create our interface and extend CrudRepository. We need not to implement our interface, its implementation will be created automatically at runtime. Find some of CrudRepository methods.

<S extends T> S save(S entity): Saves and updates the current entity and returns that entity.
Optional<T> findById(ID primaryKey): Returns the entity for the given id.
Iterable<T> findAll(): Returns all entities.
long count(): Returns the count.
void delete(T entity): Deletes the given entity.
boolean existsById(ID primaryKey): Checks if the entity for the given id exists or not.

CrudRepository has subinterface as PagingAndSortingRepository that provides additional methods to retrieve entities using the pagination and sorting abstraction.

4. Steps to Use CrudRepository

Spring boot enables JPA repository support by default. To use CrudRepository in our Spring data application we need to create an interface implementing CrudRepository and then all is done to use it. Let us discuss step wise how to use CrudRepository in our Spring data application.

4.1 Create an Interface extending CrudRepository

In our example we will perform CRUD operations on article data for demo. So I will create an interface for article extending CrudRepository as following.
public interface ArticleRepository extends CrudRepository<Article, Long>  {
}
We need not to create its implementation class. Spring will automatically create its implementation class at runtime.

4.2 Auto-Detection of JPA Repository

Spring boot can automatically detect our repository if the package of that interface is the same or sub-package of the class annotated with @SpringBootApplication and if not then we need to use @EnableJpaRepositories annotation with @SpringBootApplication. Let us understand by example.
Suppose we have a class annotated with @SpringBootApplication in the package com.concretepage as given below.
package com.concretepage;
------
@SpringBootApplication
public class MyApplication {  
------  
}
Now if we have a repository ArticleRepository and it resides in package com.concretepage or its sub-packages such as com.concretepage.repository then Spring boot will automatically detect our repository and so no need to use @EnableJpaRepositories annotation.

If we choose a package for our repository that is neither same package nor sub-package of the package of the class annotated with @SpringBootApplication, then Spring boot will not be able to detect repository classes by default. In this case we need to use @EnableJpaRepositories annotation with @SpringBootApplication. Using @EnableJpaRepositories we will configure package name in which our repository classes reside. Suppose the package of our repository classes is com.cp.repository, we will use @EnableJpaRepositories as following.
package com.concretepage;
------
@SpringBootApplication
@EnableJpaRepositories("com.cp.repository")
public class MyApplication {  
------
}
If we want to configure specific classes then we need to use basePackageClasses attribute of the @EnableJpaRepositories annotation. Suppose we have a class ArticleRepository in the package com.cp.repository, then we can configure repository using basePackageClasses as following.
package com.concretepage;
-----
import com.cp.repository.ArticleRepository;
@SpringBootApplication
@EnableJpaRepositories(basePackageClasses= {ArticleRepository.class})
public class MyApplication {  
------     
} 

4.3 Instantiate and Use CrudRepository

To instantiate our ArticleRepository that has extended CrudRepository, we can use dependency injection.
public class ArticleService {
   @Autowired
   private ArticleRepository articleRepository;
   ------
}
Now we are ready to use methods of CrudRepository. Find the example for some of its methods.
a. Create and Update:
Article savedArticle = articleRepository.save(article); 
b. Read:
Article obj = articleRepository.findById(articleId).get();
Iterable<Article> articles = articleRepository.findAll(); 
c. Delete:
articleRepository.delete(article); 

5. Custom Repository Methods

CrudRepository provides methods for generic CRUD operation and if we want to add custom methods in our interface that has extended CrudRepository, we can add in following ways.

a. We can start our query method names with find...By, read...By, query...By, count...By, and get...By. Before By we can add expression such as Distinct . After By we need to add property names of our entity.
b. To get data on the basis of more than one property we can concatenate property names using And and Or while creating method names.
c. If we want to use completely custom name for our method, we can use @Query annotation to write query.

Find the code snippet that is using the sample method name for the above scenarios.
public interface ArticleRepository extends CrudRepository<Article, Long>  {
    List<Article> findByTitle(String title);
    List<Article> findDistinctByCategory(String category);
    List<Article> findByTitleAndCategory(String title, String category);

    @Query("SELECT a FROM Article a WHERE a.title=:title and a.category=:category")
    List<Article> fetchArticles(@Param("title") String title, @Param("category") String category);
} 
The implementation class of the above methods will be created by Spring at runtime automatically.

6. @Transactional with CrudRepository

CRUD methods of CrudRepository are transactional by default. They are annotated with @Transactional annotation with default settings in implementation class at runtime. For reading operation readOnly flag is set to true. To override default transactional settings of any CrudRepository methods we need to override that method in our interface and annotate with @Transactional using required configurations. Find the example.
public interface ArticleRepository extends CrudRepository<Article, Long>  {
    @Override
    @Transactional(timeout = 8)
    Iterable<Article> findAll();
} 
Here we have configured timeout as 8 seconds to execute query without readOnly flag for findAll() method.

7. Configure Properties in application.properties File

Datasource, JPA properties and logging etc need to be configured in application.properties file located in the classpath of Spring boot application. These properties will automatically be read by Spring boot.
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/concretepage
spring.datasource.username=root
spring.datasource.password=cp

spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=1200000

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.id.new_generator_mappings = false
spring.jpa.properties.hibernate.format_sql = true

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 
In Spring Boot 2.0 release, default database pooling technology has been switched from Tomcat Pool to HikariCP. spring-boot-starter-jdbc and spring-boot-starter-data-jpa resolve HikariCP dependency by default and spring.datasource.type property has HikariDataSource as default value. The datasource properties starting with spring.datasource.* will automatically be read by Spring boot JPA. To change the Hibernate properties we will use prefix spring.jpa.properties.* with Hibernate property name. On the basis of given data source URL, Spring boot can automatically identify data source driver class. So we need not to configure diver class.
Find the properties to configure JpaBaseConfiguration and HibernateJpaAutoConfiguration in application.properties.
spring.data.jpa.repositories.enabled: It enables JPA repositories. The default value is true.
spring.jpa.database: It targets database to operate on. By default embedded database is auto-detected.
spring.jpa.database-platform: It is used to provide the name of database to operate on. By default it is auto- detected.
spring.jpa.generate-ddl: It is used to initialize schema on startup. By default the value is false.
spring.jpa.hibernate.ddl-auto: It is DDL mode used for embedded database. Default value is create-drop.
spring.jpa.hibernate.naming.implicit-strategy: It is Hibernate 5 implicit naming strategy fully qualified name.
spring.jpa.hibernate.naming.physical-strategy: It is Hibernate 5 physical naming strategy fully qualified name.
spring.jpa.hibernate.use-new-id-generator-mappings: It is used for Hibernate IdentifierGenerator for AUTO, TABLE and SEQUENCE.
spring.jpa.open-in-view: The default value is true. It binds a JPA EntityManager to the thread for the entire processing of the request.
spring.jpa.properties.*: It sets additional native properties to set on the JPA provider.
spring.jpa.show-sql: It enables logging of SQL statements. Default value is false.

8. Spring Boot REST + Spring Boot Data CrudRepository + JPA + Hibernate + MySQL CRUD Example

Find the project structure of our demo project.
Spring Boot CrudRepository Example
Find the MySQL database table used in our example.
Database Table
CREATE DATABASE IF NOT EXISTS `concretepage`;
USE `concretepage`;

CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` bigint(5) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `category` varchar(100) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=InnoDB;

INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES
	(1, 'Java Concurrency', 'Java'),
	(2, 'Spring Boot Getting Started', 'Spring Boot'),
	(3, 'Lambda Expressions Java 8 Example', 'Java 8'); 
Now find the complete code.
ArticleRepository.java
package com.concretepage.repository;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.concretepage.entity.Article;
public interface ArticleRepository extends CrudRepository<Article, Long>  {
    List<Article> findByTitle(String title);
    List<Article> findDistinctByCategory(String category);
    List<Article> findByTitleAndCategory(String title, String category);
} 
Article.java
package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="articles")
public class Article implements Serializable { 
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="article_id")
        private long articleId;  
	@Column(name="title")
        private String title;
	@Column(name="category")	
	private String category;
	public long getArticleId() {
		return articleId;
	}
	public void setArticleId(long articleId) {
		this.articleId = articleId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
}  
IArticleService.java
package com.concretepage.service;
import java.util.List;
import com.concretepage.entity.Article;
public interface IArticleService {
     List<Article> getAllArticles();
     Article getArticleById(long articleId);
     boolean addArticle(Article article);
     void updateArticle(Article article);
     void deleteArticle(int articleId);
} 
ArticleService.java
package com.concretepage.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.concretepage.entity.Article;
import com.concretepage.repository.ArticleRepository;
@Service
public class ArticleService implements IArticleService {
	@Autowired
	private ArticleRepository articleRepository;
	@Override
	public Article getArticleById(long articleId) {
		Article obj = articleRepository.findById(articleId).get();
		return obj;
	}	
	@Override
	public List<Article> getAllArticles(){
		List<Article> list = new ArrayList<>();
		articleRepository.findAll().forEach(e -> list.add(e));
		return list;
	}
	@Override
	public synchronized boolean addArticle(Article article){
	        List<Article> list = articleRepository.findByTitleAndCategory(article.getTitle(), article.getCategory()); 	
                if (list.size() > 0) {
    	           return false;
                } else {
    	        articleRepository.save(article);
    	        return true;
       }
	}
	@Override
	public void updateArticle(Article article) {
		articleRepository.save(article);
	}
	@Override
	public void deleteArticle(int articleId) {
		articleRepository.delete(getArticleById(articleId));
	}
} 
ArticleController.java
package com.concretepage.controller;
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.stereotype.Controller;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.util.UriComponentsBuilder;
import com.concretepage.entity.Article;
import com.concretepage.service.IArticleService;
@Controller
@RequestMapping("user")
public class ArticleController {
	@Autowired
	private IArticleService articleService;
	@GetMapping("article/{id}")
	public ResponseEntity<Article> getArticleById(@PathVariable("id") Integer id) {
		Article article = articleService.getArticleById(id);
		return new ResponseEntity<Article>(article, HttpStatus.OK);
	}
	@GetMapping("articles")
	public ResponseEntity<List<Article>> getAllArticles() {
		List<Article> list = articleService.getAllArticles();
		return new ResponseEntity<List<Article>>(list, HttpStatus.OK);
	}
	@PostMapping("article")
	public ResponseEntity<Void> addArticle(@RequestBody Article article, UriComponentsBuilder builder) {
                boolean flag = articleService.addArticle(article);
                if (flag == false) {
        	   return new ResponseEntity<Void>(HttpStatus.CONFLICT);
                }
                HttpHeaders headers = new HttpHeaders();
                headers.setLocation(builder.path("/article/{id}").buildAndExpand(article.getArticleId()).toUri());
                return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	@PutMapping("article")
	public ResponseEntity<Article> updateArticle(@RequestBody Article article) {
		articleService.updateArticle(article);
		return new ResponseEntity<Article>(article, HttpStatus.OK);
	}
	@DeleteMapping("article/{id}")
	public ResponseEntity<Void> deleteArticle(@PathVariable("id") Integer id) {
		articleService.deleteArticle(id);
		return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
	}	
} 
MyApplication.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {  
    public static void main(String[] args) {
	 SpringApplication.run(MyApplication.class, args);
    }       
} 

9. Client Code with RestTemplate

RestClientUtil.java
package com.concretepage.client;
import java.net.URI;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.concretepage.entity.Article;
public class RestClientUtil {
    public void getArticleByIdDemo() {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article/{id}";
        HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
        ResponseEntity<Article> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article.class, 1);
        Article article = responseEntity.getBody();
        System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                 +", Category:"+article.getCategory());      
    }
    public void getAllArticlesDemo() {
	HttpHeaders headers = new HttpHeaders();
	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/articles";
        HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
        ResponseEntity<Article[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article[].class);
        Article[] articles = responseEntity.getBody();
        for(Article article : articles) {
              System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                      +", Category: "+article.getCategory());
        }
    }
    public void addArticleDemo() {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article";
	Article objArticle = new Article();
	objArticle.setTitle("Spring REST Security using Hibernate");
	objArticle.setCategory("Spring");
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(objArticle, headers);
        URI uri = restTemplate.postForLocation(url, requestEntity);
        System.out.println(uri.getPath());    	
    }
    public void updateArticleDemo() {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article";
	Article objArticle = new Article();
	objArticle.setArticleId(1);
	objArticle.setTitle("Update:Java Concurrency");
	objArticle.setCategory("Java");
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(objArticle, headers);
        restTemplate.put(url, requestEntity);
    }
    public void deleteArticleDemo() {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/user/article/{id}";
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(headers);
        restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Void.class, 4);        
    }
    public static void main(String args[]) {
    	RestClientUtil util = new RestClientUtil();
        //util.getArticleByIdDemo();
    	//util.addArticleDemo();
    	//util.updateArticleDemo();
    	//util.deleteArticleDemo();
    	util.getAllArticlesDemo();    	
    }    
} 

10. Test Application

To test the application, first create table in MySQL as given in the example. Then we can run REST web service in following ways.
1. Using Eclipse: Download the project source code using the download link given at the end of article. Import the project into eclipse. Using command prompt, go to the root folder of the project and run.
mvn clean eclipse:eclipse 
and then refresh the project in eclipse. Run Main class MyApplication by clicking Run as -> Java Application. Tomcat server will be started.

2. Using Maven Command: Download the project source code. Go to the root folder of the project using command prompt and run the command.
mvn spring-boot:run 
Tomcat server will be started.

3. Using Executable JAR: Using command prompt, go to the root folder of the project and run the command.
mvn clean package 
We will get executable JAR spring-boot-demo-0.0.1-SNAPSHOT.jar in target folder. Run this JAR as
java -jar target/spring-boot-demo-0.0.1-SNAPSHOT.jar 
Tomcat server will be started.

Now we are ready to test the application. To run client, go to the RestClientUtil class in eclipse and click on Run as -> Java Application.
We can also test application using Postman. Find the print screen.
Spring Boot CrudRepository Example

11. References

Accessing Data with JPA
Spring Data CrudRepository Example
Spring Boot REST + JPA + Hibernate + MySQL Example

12. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us