Spring Data JPA Bootcamp

Agenda

  • Introduction
  • Getting Required Dependencies
  • Configuration
  • CRUD
  • Query Methods
  • Creating Database Queries from Method Names
  • @Query Annotation
  • Named Queries
  • Sorting
  • Pagination

What is Spring Data JPA ?

Spring Data JPA is not a JPA provider. It is a library / framework that adds an extra layer of abstraction on the top of our JPA provider. If we decide to use Spring Data JPA, the repository layer of our application contains three layers that are described in the following:

  • Spring Data JPA provides support for creating JPA repositories by extending the Spring Data repository interfaces.
  • Spring Data Commons provides the infrastructure that is shared by the datastore specific Spring Data projects.
  • The JPA Provider implements the Java Persistence API.

What components do we need ?

If we want to implement a persistence layer that uses Spring Data JPA, we need the following components:

  • The JDBC driver provides a database specific implementation of the JDBC API.
  • The JPA Provider implements the Java Persistence API. We use Hibernate because it is the most common JPA provider.
  • Spring Data JPA hides the used JPA provider behind its repository abstraction.

build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.hibernate:hibernate-entitymanager:4.1.7.Final'
    compile 'mysql:mysql-connector-java:5.1.13'
    compile 'org.springframework.data:spring-data-jpa:1.11.4.RELEASE'

}

Configuration

  • We have to create configuration class that configures the persistence layer of our application:
@Configuration
class PersistenceContext {
 
    //Configure the required beans here
}
  • Configure the datasource bean.
    @Bean
    DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/poc");
        driverManagerDataSource.setUsername("root");
        return driverManagerDataSource;
    }
  • Configure the entity manager factory bean.
    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = 
                new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("com.springdata.entity");

        Properties jpaProperties= new Properties();
        jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
        jpaProperties.setProperty("hibernate.show_sql","true");
        jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");

        entityManagerFactoryBean.setJpaProperties(jpaProperties);
        return entityManagerFactoryBean;
    }
  • Configure the transaction manager bean.
    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
  • Enable Spring Data JPA by annotating the PersistenceContext class with the@EnableJpaRepositories annotation.
@Configuration
@EnableJpaRepositories("com.springdata.repositories")
class PersistenceContext {
     
    //The beans are configured here
}

PersistenceContext.java

package com.springdata.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@EnableJpaRepositories("com.springdata.repositories")
public class PersistenceContext {

    @Bean
    DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/poc");
        driverManagerDataSource.setUsername("root");
        return driverManagerDataSource;
    }

    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("com.springdata.entity");

        Properties jpaProperties= new Properties();
        jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
        jpaProperties.setProperty("hibernate.show_sql","true");
        jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");

        entityManagerFactoryBean.setJpaProperties(jpaProperties);
        return entityManagerFactoryBean;
    }

    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
}

Creating an entity

We have to create an entity class that contains the information of a single Employee entry.

package com.springdata.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

@Entity
public class Employee implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    private Integer age;

    private Integer salary;

    //getters and setters
    }

We are now ready to create our first Spring Data JPA repository. We can create the repository that provides CRUD operations for Todo objects by using one of the following methods:

  • Create an interface that extends the CrudRepository interface.
  • Create an interface that extends the Repository interface and add the required methods to the created interface.

 

Creating a repository (Cont.)

Extending the CrudRepository Interface

If we create our repository by extending the CrudRepository interface, we have to provide two type parameters:

  • The type of the entity that is managed by our repository.
  • The type of the entity’s id field.
package com.springdata.repositories;



import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
}

Extending the Repository Interface

  • Provide two type parameters managed entity (Employee) and the type of the entity’s id field (Integer).
  • Add the required methods to the repository interface:
package com.springdata.repositories;
 
import java.util.List;
import java.util.Optional;
 
interface EmployeeRepository extends Repository<Employee, Integer> {
 
    void delete(Employee deleted);
 
    List<Employee> findAll();
 
    Employee save(Employee persisted);
}

Query Methods

Query methods are methods that find information from the database and are declared on the repository interface.

package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {

    List<Employee> findByName(String name);

    Employee findById(Integer id);
}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {

    @Query("SELECT name from Employee where id=:id")
    String findById(@Param("id") Integer id);
}
  • Spring Data has pretty versatile support for different return values that we can leverage when we are adding query methods to our Spring Data JPA repositories.
  • We can pass parameters to our database queries by using either position based parameter binding or named parameters.
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface EmployeeRepository  extends Repository<Employee, Integer> {

    @Query("SELECT id,name from Employee where id=:id")
    List<Object[]> findById(@Param("id") Integer id);
}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

public interface EmployeeRepository  extends Repository<Employee, Integer> {

    @Query("SELECT e from Employee e where id=:id")
    Employee findById(@Param("id") Integer id);
}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

public interface EmployeeRepository  extends Repository<Employee, Integer> {

    @Query("SELECT Count(*) from Employee e where name=:name")
    Integer countByName(@Param("name") String name);
}

Creating Database queries from method name

  • The name of our query method must start with one of the following prefixes: find…By, read…By, query…By, count…By, and get…By.
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {

    int countById(Integer id);
}
  • If we want to limit the number of returned query results, we can add the First or the Top keyword before the first By word
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {

    List<Employee> findFirstByName(String name);

    List<Employee> findFirst3ByName(String name);
}
  • If we want to select unique results, we have to add the Distinct keyword before the first By word. For example, findDistinctEmployeeByName means that we want to select all unique titles that are found from the database.
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    Employee findDistinctByName(String name);
}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByNameOrAge(String name, Integer age);
}
  • Or Operation
  • And Operation
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByNameAndAge(String name, Integer age);
}
  • Between
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByAgeBetween(int min,int max);
}
  • LessThan
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByAgeLessThan(int age);
}

GreaterThanEquals

package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByAgeGreaterThanEqual(Integer age);
}
  • Like
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByNameLike(String name);

}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByNameNot(String name);

}
  • Not
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByAgeIn(List<Integer> ageList);

}
  • In
  • IgnoreCase
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findByNameIgnoreCase(String name);

}

Named Queries

If we want to create a SQL query, we must follow these steps:

  1. Annotate the entity with the @NamedNativeQuery annotation.
  2. Set the name of the named query (Employee.findByNameIs) as the value of the @NamedNativeQuery annotation's named attribute.
  3. Set the SQL Query as the value of @NamedNativeQuery annotation name attribute.
  4. Set the returned entity class as the value of @NamedNativeQuery resultClass attribute.

Named Queries

package com.springdata.entity;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@NamedNativeQuery(name = "Employee.findByNameIs",
            query = "SELECT * from Employee where name = :name",
            resultClass = Employee.class)
public class Employee implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;
    private Integer age;
    private Integer salary;

    // getters and setters    
}
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {

    List<Employee> findByNameIs(@Param("name") String name);
}

Named Queries

Sorting

we can sort the query results of our database queries by using the OrderBy keyword. We can use the OrderBy keyword by following these steps:

  1. Append the OrderBy keyword to the method name of our query method.
  2. Append the name of the property to the method name of our query method and transform its first letter into uppercase. If we want to order our query results by using the title of a todo entry, we have to append the string: Title to the method name of our query method.
  3.  

Sorting

  • Describe the sort direction. If we want to sort the query results in ascending order, we have to append the keyword Asc to the method name of our query method. On the other hand, if we want to sort the query results in descending order, we have to append the keyword Desc to the method name of our query method.
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
    
    List<Employee> findByNameOrderByIdDesc(String name);
}

Sorting Query Results With the Sort Class

We can sort our query results by following these steps:

  • Obtain the Sort object that describes the sorting options of the invoked database query.
  • Pass the Sort object forward to the correct repository method as a method parameter.
package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    List<Employee> findAll(Sort sort);
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeRepository employeeRepository=applicationContext.getBean(EmployeeRepository.class);
List<Employee> list = employeeRepository.findAll(new Sort(Sort.Direction.DESC,"age").and(
                new Sort(Sort.Direction.ASC,"name")));

System.out.println(list);

Pagination

If we want create the Pageable object manually, the service class (or other component) that wants to paginate the query results, which are returned by a Spring Data JPA repository, must create the Pageable object and pass it forward to the invoked repository method.

package com.springdata.repositories;

import com.springdata.entity.Employee;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository  extends CrudRepository<Employee, Integer> {

    Page<Employee> findAll(Pageable pageable);
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeRepository employeeRepository=applicationContext.getBean(EmployeeRepository.class);
Page<Employee> employeePage= employeeRepository.findAll(new PageRequest(0,3,new Sort(Sort.Direction.DESC,"id")));
List<Employee> employeeList=employeePage.getContent();
System.out.println(employeeList);
Made with Slides.com