Thursday 19 September 2019

Spring Data JPA Specifications Example


JPA 2 introduces a criteria API that you can use to build queries programmatically. By writing a criteria, you define the where clause of a query for a domain class. Taking another step back, these criteria can be regarded as a predicate over the entity that is described by the JPA criteria API constraints.

Spring Data JPA takes the concept of a specification from Eric Evans' book, “Domain Driven Design”, following the same semantics and providing an API to define such specifications with the JPA criteria API. To support specifications, you can extend your repository interface with the JpaSpecificationExecutor interface, as follows:

public interface EmployeeRepository extends JpaSpecificationExecutor<Employee>, PagingAndSortingRepository<Employee, Integer>{

}


Implement Specification interface. I created Specification object using anonymous class.
public static Specification<Employee> getByFirstAndLastName(String firstName, String lastName) {
    return new Specification<Employee>() {

        @Override
        public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            List<Predicate> predicates = new ArrayList<>();

            Path firstNameExpression = root.get("firstName");
            Path lastNameExpression = root.get("lastName");

            predicates.add(criteriaBuilder.equal(firstNameExpression, firstName));
            predicates.add(criteriaBuilder.equal(lastNameExpression, lastName));

            if (predicates.isEmpty()) {
                return criteriaBuilder.conjunction();
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

        }

    };
}

Use the specification to query for the results.

List<Employee> emps = employeeRepository.findAll(EmployeeSpecifications.getByFirstAndLastName("Gopi", "Battu"));

Find the below working example.


Employee.java
package com.sample.app.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Entity
@Table(name = "my_employee")
@EntityListeners(AuditingEntityListener.class)
public class Employee {
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Integer id;

 @Column(name = "first_name")
 private String firstName;

 @Column(name = "last_name")
 private String lastName;

 @Column(name = "age")
 private Integer age;

 @Column(name = "salary")
 private Double salary;

 @CreatedDate
 @Column(name = "created_date")
 private Date createdTime;

 @LastModifiedDate
 @Column(name = "last_modified_date")
 private Date lastModifiedTime;

 @CreatedBy
 private String createdBy;

 @LastModifiedBy
 private String modifiedBy;

 public Integer getId() {
  return id;
 }

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

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public Integer getAge() {
  return age;
 }

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

 public Double getSalary() {
  return salary;
 }

 public void setSalary(Double salary) {
  this.salary = salary;
 }

 public static EmployeeBuilder builder() {
  return new EmployeeBuilder();
 }

 public Date getCreatedTime() {
  return createdTime;
 }

 public void setCreatedTime(Date createdTime) {
  this.createdTime = createdTime;
 }

 public Date getLastModifiedTime() {
  return lastModifiedTime;
 }

 public void setLastModifiedTime(Date lastModifiedTime) {
  this.lastModifiedTime = lastModifiedTime;
 }

 public static class EmployeeBuilder {
  private Employee emp;

  public EmployeeBuilder() {
   emp = new Employee();
  }

  public EmployeeBuilder firstName(String firstName) {
   emp.setFirstName(firstName);
   return this;
  }

  public EmployeeBuilder lastName(String lastName) {
   emp.setLastName(lastName);
   return this;
  }

  public EmployeeBuilder age(int age) {
   emp.setAge(age);
   return this;
  }

  public EmployeeBuilder salary(double salary) {
   emp.setSalary(salary);
   return this;
  }

  public Employee build() {
   return emp;
  }
 }

 public String getCreatedBy() {
  return createdBy;
 }

 public void setCreatedBy(String createdBy) {
  this.createdBy = createdBy;
 }

 public String getModifiedBy() {
  return modifiedBy;
 }

 public void setModifiedBy(String modifiedBy) {
  this.modifiedBy = modifiedBy;
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [id=").append(id).append(", firstName=").append(firstName).append(", lastName=")
    .append(lastName).append(", age=").append(age).append(", salary=").append(salary)
    .append(", createdTime=").append(createdTime).append(", lastModifiedTime=").append(lastModifiedTime)
    .append(", createdBy=").append(createdBy).append(", modifiedBy=").append(modifiedBy).append("]");
  return builder.toString();
 }

}


EmployeeRepository.java
package com.sample.app.repository;

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

import com.sample.app.model.Employee;

public interface EmployeeRepository extends JpaSpecificationExecutor<Employee>, PagingAndSortingRepository<Employee, Integer>{

}


Employeespecifications.java
package com.smaple.app.jpa.specification;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.data.jpa.domain.Specification;

import com.sample.app.model.Employee;

public class EmployeeSpecifications {

 public static Specification<Employee> getByFirstAndLastName(String firstName, String lastName) {
  return new Specification<Employee>() {

   @Override
   public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    List<Predicate> predicates = new ArrayList<>();

    Path firstNameExpression = root.get("firstName");
    Path lastNameExpression = root.get("lastName");

    predicates.add(criteriaBuilder.equal(firstNameExpression, firstName));
    predicates.add(criteriaBuilder.equal(lastNameExpression, lastName));

    if (predicates.isEmpty()) {
     return criteriaBuilder.conjunction();
    }

    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

   }

  };
 }

}


App.java
package com.sample.app;

import java.util.List;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.sample.app.model.Employee;
import com.sample.app.repository.EmployeeRepository;
import com.smaple.app.jpa.specification.EmployeeSpecifications;

@SpringBootApplication
public class App {

 public static void main(String args[]) {
  SpringApplication.run(App.class, args);
 }

 @Bean
 public CommandLineRunner demo(EmployeeRepository employeeRepository) {
  return (args) -> {
   Employee emp1 = Employee.builder().firstName("Ram").lastName("Gurram").age(32).salary(100000.23).build();
   Employee emp2 = Employee.builder().firstName("Joel").lastName("Chelli").age(43).salary(60000).build();
   Employee emp3 = Employee.builder().firstName("Gopi").lastName("Battu").age(32).salary(1000000).build();
   Employee emp4 = Employee.builder().firstName("Bomma").lastName("Srikanth").age(39).salary(60000).build();
   Employee emp5 = Employee.builder().firstName("Surendra").lastName("Sami").age(32).salary(100000.23).build();
   Employee emp6 = Employee.builder().firstName("Bhadri").lastName("Venakata RamaRao").age(32)
     .salary(100000.23).build();
   Employee emp7 = Employee.builder().firstName("Sushmithaa").lastName("Kulakarni").age(39).salary(100000.23)
     .build();

   employeeRepository.save(emp1);
   employeeRepository.save(emp2);
   employeeRepository.save(emp3);
   employeeRepository.save(emp4);
   employeeRepository.save(emp5);
   employeeRepository.save(emp6);
   employeeRepository.save(emp7);

   for (Employee emp : employeeRepository.findAll()) {
    System.out.println(emp);
   }

   System.out.println("***************************");
   List<Employee> emps = employeeRepository
     .findAll(EmployeeSpecifications.getByFirstAndLastName("Gopi", "Battu"));
   for (Employee emp : emps) {
    System.out.println(emp);
   }

  };
 }
}


application.properties
logging.level.root=WARN
logging.level.org.hibernate=ERROR

## H2 specific properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;

spring.datasource.username=krishna
spring.datasource.password=password123

spring.datasource.driverClassName=org.h2.Driver

## JPA specific properties
# Creates the schema, destroying previous data.
spring.jpa.hibernate.ddl-auto=create

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

#spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true

## Database connection pooling properties
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=3


Total project structure looks like below.


Run App.java, you can get below messages in console.
Employee [id=1, firstName=Ram, lastName=Gurram, age=32, salary=100000.23, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=2, firstName=Joel, lastName=Chelli, age=43, salary=60000.0, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=3, firstName=Gopi, lastName=Battu, age=32, salary=1000000.0, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=4, firstName=Bomma, lastName=Srikanth, age=39, salary=60000.0, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=5, firstName=Surendra, lastName=Sami, age=32, salary=100000.23, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=6, firstName=Bhadri, lastName=Venakata RamaRao, age=32, salary=100000.23, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
Employee [id=7, firstName=Sushmithaa, lastName=Kulakarni, age=39, salary=100000.23, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]
***************************
Employee [id=3, firstName=Gopi, lastName=Battu, age=32, salary=1000000.0, createdTime=null, lastModifiedTime=null, createdBy=null, modifiedBy=null]

You can download the complete working application from this link.



Previous                                                    Next                                                    Home

No comments:

Post a Comment