Agenda
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:
What components do we need ?
If we want to implement a persistence layer that uses Spring Data JPA, we need the following components:
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
@Configuration
class PersistenceContext {
//Configure the required beans here
}
@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;
}
@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:
Creating a repository (Cont.)
If we create our repository by extending the CrudRepository interface, we have to provide two type parameters:
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
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);
}
Exercise 1
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);
}
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);
}
Exercise 2
Creating Database queries from method name
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
Exercise 3
Implement following methods for Person repository:
If we want to create a SQL query, we must follow these steps:
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);
}
Exercise 4
Implement Named query to fetch an Employee Object for a firstname and lastname.
JPA Criteria API
We can create database queries with the JPA Criteria API by following these steps:
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.Repository;
public interface EmployeeJPARepository extends Repository<Employee,Integer>
,JpaSpecificationExecutor<Employee>{
}
2. Specify the conditions of the invoked database query.
3. Invoke the database query.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Predicate predicate = cb.gt(path,5);
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Path path1= root.get("name");
Predicate predicate = cb.and(cb.equal(path1,"Peter1"),cb.lt(path,7));
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Path path1= root.get("name");
Predicate predicate = cb.between(path,1,5);
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
Exercise 5
Use JPA Criteria API for following operations for person
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:
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);
}
We can sort our query results by following these steps:
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);
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);
Exercise 6
Auditing
The source code of the DateTimeService interface looks as follows:
package com.springdata.demo;
import java.time.ZonedDateTime;
public interface DateTimeService {
ZonedDateTime getCurrentDateAndTime();
}
The CurrentTimeDateTimeService class implements the DateTimeService interface. Its getCurrentDateAndTime() method simply returns the current date and time.
package com.springdata.demo;
import java.time.ZonedDateTime;
public class CurrentDateTimeService implements DateTimeService {
@Override
public ZonedDateTime getCurrentDateAndTime() {
return ZonedDateTime.now();
}
}
package com.springdata.demo;
import org.springframework.data.auditing.DateTimeProvider;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class AuditingDateTimeProvider implements DateTimeProvider {
private final DateTimeService dateTimeService;
public AuditingDateTimeProvider(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
@Override
public Calendar getNow() {
return GregorianCalendar.from(dateTimeService.getCurrentDateAndTime());
}
}
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@EnableJpaRepositories("com.springdata.repositories")
public class PersistenceContext {
// Other beans
@Bean
DateTimeService currentTimeDateTimeService() {
return new CurrentDateTimeService();
}
@Bean
DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) {
return new AuditingDateTimeProvider(dateTimeService);
}
}
we need to create the DateTimeProvider bean and enable the auditing support of Spring Data. We can do this by making the following changes to the configuration class that configures the persistence layer of our example application:
Employee Entity
package com.springdata.entity;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.Date;
@Entity
@NamedNativeQuery(name = "Employee.findByNameIs",
query = "SELECT * from Employee where name = :name",
resultClass = Employee.class)
@EntityListeners(AuditingEntityListener.class)
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Integer age;
private Integer salary;
@CreatedDate
private Date creationTime;
@LastModifiedDate
private Date modificationTime;
//getters and setters
}
Exercise 7
Implement dateCreated and lastUpdated auditing for Person.
Creating a custom repository
package com.springdata.demo;
public class EmployeeSearchResultDTO {
private String name;
private Integer age;
public EmployeeSearchResultDTO() {}
//getters and setters
}
package com.springdata.demo;
import java.util.List;
public interface CustomEmployeeRepository {
List<EmployeeSearchResultDTO> searchTerm(String searchTerm);
}
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@ComponentScan("com.springdata.component")
@EnableJpaRepositories(value = "com.springdata.repositories",
repositoryImplementationPostfix="Impl")
public class PersistenceContext {
//Other beans
@Bean
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
package com.springdata.component;
import com.springdata.demo.CustomEmployeeRepository;
import com.springdata.demo.EmployeeSearchResultDTO;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class EmployeeRepositoryImpl implements CustomEmployeeRepository ,InitializingBean{
private static final String SEARCH_TODO_ENTRIES = "SELECT name, age FROM Employee t WHERE " +
"LOWER(t.name) LIKE LOWER(CONCAT('%',:searchTerm, '%')) " +
"ORDER BY t.id ASC";
private final NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
public EmployeeRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional(readOnly = true)
@Override
public List<EmployeeSearchResultDTO> searchTerm(String searchTerm) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("searchTerm", searchTerm);
List<EmployeeSearchResultDTO> searchResults = jdbcTemplate.query(SEARCH_TODO_ENTRIES,
queryParams,
new BeanPropertyRowMapper<>(EmployeeSearchResultDTO.class)
);
return searchResults;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(">>>>>>>>>>>>>>>>setting" );
}
}
package com.springdata.repositories;
import com.springdata.demo.CustomEmployeeRepository;
import com.springdata.entity.Employee;
import org.springframework.data.repository.Repository;
public interface EmployeeRepository extends Repository<Employee, Integer>,
CustomEmployeeRepository {
Integer count();
}
Exercise 8
Add a custom repository for person entity get firstname and lastname of the personswhose name starts with a specific character.