SpringData Series Part 2: Property Traversal

Property Traversal


We are aware of the fact that if we want to create a query based on entity bean properties which are not present Repository interface (CrudRepository,JpaRepository etc). We can do it very easily in SpringData. Just we need to declare a method in our custom Repository which must have to obey a pattern. then Spring data create the query for us on the fly.

Pattern is Queryname<java property><Operation>,


In this Article, we will discuss the details of this Query derivation technique.

SpringData has an inbuilt data stores(JPA,MongoDB) specific QureyTransalorFactory ,which will translate the method written in the custom repository  to store specific query.

Let say we have a Person Entity and has a Person Repository and underlying dataStore is JPA
package com.example.person;

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

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Person {
           
              @Id
              @GeneratedValue(strategy=GenerationType.AUTO)
            private Long id;          
            private String name;
            private String country;
            private String gender;
           
            @OneToMany(mappedBy="person",targetEntity=Hobby.class,
                               fetch=FetchType.EAGER,cascade=CascadeType.ALL)
            List<Hobby> hobby;
           
            public String getName() {
                        return name;
            }
            public void setName(String name) {
                        this.name = name;
            }
            public String getCountry() {
                        return country;
            }
            public void setCountry(String country) {
                        this.country = country;
            }
            public String getGender() {
                        return gender;
            }
            public void setGender(String gender) {
                        this.gender = gender;
            }
           
           
            public Long getId() {
                        return id;
            }
            public void setId(Long id) {
                        this.id = id;
            }
           
           
            public List<Hobby> getHobby() {
                        return hobby;
            }
            public void setHobby(List<Hobby> hobby) {
                        this.hobby = hobby;
            }
           
            public void addHobby(Hobby ihobby)
            {
                        if(hobby == null)
                        {
                                    hobby = new ArrayList<Hobby>();
                        }
                        hobby.add(ihobby);
            }
            @Override
            public String toString() {
                        return "Person [id=" + id + ", name=" + name + ", country=" + country + ", gender=" + gender + "]";
            }
           
           

}

package com.example.person;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class Hobby {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
  
   @ManyToOne
   @JoinColumn(name="person_id")
private Person person;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}





}


Repository

package com.example.repo;

import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import com.example.person.Person;


public interface PersonRepositary extends CrudRepository<Person, Long> {
           
            List<Person> findByCountryContains(String country);
                       

}


Now, When QueryTranslator encounters the method findByCountryContains, it performs following Steps

1.    Strip the section findBy.
2.    Now try to find an exact match for the stripped section and Person entity’s property. As Person does not have any property called CountryContains it goes to Step 3.
3.    Again query translator split the rest section based on camelCase pattern from the tail so it has now two tokens (Country and Contains)
4.    Now query translator tries to match country with Person entity properties now it finds an exact match so it's taken this phrase as one of the filter criteria.
5.    As Contains is a predefined combining criterion based on the underlying data store. So query translator understands the same has and put a like check.
6.    As underlying Store is JPA now query translator generate a query
Select p from person p where p.country like ?1




Spring Data’s QueryTranslator is very powerful it can also derive a query using nested bean property.
Let check the Person bean again, we have a mapping between Person and Hobby it is a one to many relationships.
suppose we want to create a query which will fetch person based on a hobby.

To do this ,we just have to create a method in Person repository,
package com.example.repo;

import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import com.example.person.Person;


public interface PersonRepositary extends CrudRepository<Person, Long> {
           
            List<Person> findByCountryContains(String country);
           
            List<Person> findPersonByHobbyName(String hobby);
           

}

Now query translator tries to find a match “HobbyName” as it is not matched, again it strips this phrase to two tokens hobby and name then it tries to match hobby now it finds a List in Person entity then it goes into the Hobby Entity and found a property called name so it creates a query like following
Hibernate:
    select
        person0_.id as id1_1_,
        person0_.country as country2_1_,
        person0_.gender as gender3_1_,
        person0_.name as name4_1_
    from
        person person0_
    left outer join
        hobby hobby1_
            on person0_.id=hobby1_.person_id
    where
        hobby1_.name=?




Query translator is powerful but it has one shortcoming, let say we have added an additional property hobbyName in person now Query translator finds a match hobbyName in first place so it tries to filter against this property

So then query would be
Select p from Person p where p.hobbyName=?1

Which is wrong so overcome this situation we can introduce a _(underscore) to demarcation the traversal path

Method name will be,
List<Person> findPersonByHobby_Name(String hobby);
Now QueryTranslator understands hobby and name are two different tokens.


Spring Data Series: Spring Data In Nutshell

Spring Data Overview

In this Series, we will deep dive into Spring Data Architecture.  In this Article, we will concentrate on features of Spring Data.

Why Spring Data?
Spring data is an Umbrella which takes care of SQL and NOSQL databases and reduces the effort to use them. As Spring community’s motto is to reduce boilerplate code so Spring community invented Spring data which is take one step forward than database template.  In JPA template although spring hides the creation EntityManagerFactory managing Entity manager but the problem is we still have to maintain a DAO layer and provide CRUD operations for each entity. CRUD operations are similar in nature you have framed it in your mind but still, have to write or copy and paste same code over and over for different Entities.
Spring address this problem and introduce Spring data which says you don’t have to write redundant code for CRUD just define an interface, Spring creates the actual implementation on the fly. Pure magic.

Spring Data Repository:
Spring data introduce Repository concept, Repository act like Adapter It takes the Entity and makes Persistence layer specific call , it may be JPA or MongoDB etc. The beauty of Repository is we just to create Interface which will extend Spring Data’s Repository marker interface. By doing so you instruct spring to find those interfaces which will extend Repository interface and add them as a Spring bean, by creating a Proxy and delegate the call to actual implementation. But as a developer we don’t have to write actual implementation just we need to mention what methods we need Spring takes care of that by Create and Derivation approach.

Choosing underlying Persistence Layer:
As I told earlier Spring data is like umbrella it contains different types of persistence layers like JPA,MongoDB so as a client you need to instruct Spring data to which persistence layer you want to use. So Spring data provide different Annotations for different Repository So If you want to use JPA just annotated your main class with @EnableJpaRepositories or If you prefer MongoDB then @EnableMongoRepositories.  Now Spring Data understand you need JPA repository or Mongo repository so it can create JPA or MONGO Specific calls. Actually, Behind the Scene Spring data has an implementation of each Repository like SimpleJpaRepository or SimpleMongoRepository, Spring data internally delegates the call to actual implementation.

Different Repository:
Spring Data provide various Repository interfaces which extend the Marker interface Repository. You can choose them based on your need. Just create a custom interface and extend one of the Repository interfaces,

Two important Repository interfaces
1.    CRUDRepository
2.    PagingAndSortingRepository

1 .   CRUDRepository Use when you need to perform CRUD operation (Create,Read,Update,Delete)
public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {                                                                                                                        
    <S extends T> S save(S entity);                                                                                                                       
    T findOne(ID primaryKey);                                                                                                                       
    Iterable<T> findAll();
    Long count();                                                                                                                        
    void delete(T entity);                                                                                                                       
    boolean exists(ID primaryKey);
                                                                                                                       
}    // … more functionality omitted.

2.    PagingAndSortingRepository : Use when you want to use paging and sorting.

            public interface PagingAndSortingRepository<T, ID extends Serializable>
              extends CrudRepository<T, ID> {   
              Iterable<T> findAll(Sort sort);          
              Page<T> findAll(Pageable pageable);
            }
Assumption
1.    We use JPA as Repository
2.    We use Mysql Dbas persistence layer

So introduce Spring Data in your project following Steps are needed.


1.    Add Spring data JPA dependencies into Pom.xml

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2.    Add target database driver. We use MySql so we add following
<dependency>
                 <groupId>mysql</groupId>
                 <artifactId>mysql-connector-java</artifactId>
                     <scope>runtime</scope>
              </dependency>





3.    Create a Person Entity
4.    Create a Person table in MySql database
5.    Create a PersonRepository which extends CRUDRepository.
6.    Put @EnableJpaRepositories over main class


Now run application data will successfully save in data base.




Java Code:

As I use Spring boot I use database details  in application.properties 

spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

package com.example.cab;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Person {
           
              @Id
              @GeneratedValue(strategy=GenerationType.AUTO)
            private Long id;          
            private String name;
            private String country;
            private String gender;
            public String getName() {
                        return name;
            }
            public void setName(String name) {
                        this.name = name;
            }
            public String getCountry() {
                        return country;
            }
            public void setCountry(String country) {
                        this.country = country;
            }
            public String getGender() {
                        return gender;
            }
            public void setGender(String gender) {
                        this.gender = gender;
            }
           
           
            public Long getId() {
                        return id;
            }
            public void setId(Long id) {
                        this.id = id;
            }
            @Override
            public String toString() {
                        return "Person [id=" + id + ", name=" + name + ", country=" + country + ", gender=" + gender + "]";
            }
           
           

}




package com.example.cab;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface PersonRepositary extends CrudRepository<Person, Long> {
               
                List<Person> findByCountryContains(String country);

}

package com.example.cab;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories
public class PersonApplication {
      
       private static final Logger log = LoggerFactory.getLogger(PersonApplication.class);
      
       @Bean
       public CommandLineRunner demo(PersonRepositary repository) {
               Person p = new Person();
               p.setName("Samir mitra");
               p.setCountry("America");
               p.setGender("male");
               
               repository.save(p);
              log.info("Person created" + p);
              return null;
       }

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


Output: Person createdPerson [id=4, name=Samir mitra, country=America, gender=male]


Adding Extra filter query in Interface: Sometimes you want to create a find query based on a property which is not in CRUDRepository interface. You have provision to do so just declare a method in your custom interface which must have to obey a Syntax. then Spring data create the query for you on the fly.

Syntax is Queryname<java property><Operation>,
 like we create a query in Person Repository
 List<Person> findByCountryContains(String country);

If you run application Output will be
Hibernate:
    select
        person0_.id as id1_0_,
        person0_.country as country2_0_,
        person0_.gender as gender3_0_,
        person0_.name as name4_0_
    from
        person person0_
    where
        person0_.country like ?
2016-09-24 21:37:32.334  INFO 6384 --- [           main] com.example.cab.PersonApplication        : Person Person [id=1, name=Shamik mitra, country=India, gender=male]
2016-09-24 21:37:32.334  INFO 6384 --- [           main] com.example.cab.PersonApplication        : Person Person [id=2, name=Swastika mitra, country=India, gender=Female]
2016-09-24 21:37:32.334  INFO 6384 --- [           main] com.example.cab.PersonApplication        : Person Person [id=3, name=Swastika mitra, country=India, gender=Female]



Delete Operation: 
When the discussion comes to delete a curious mind often asks, Spring data support soft delete? The answer is no Spring data only Support physical delete as of now.

Use Handcrafted Query: If you want you do not use Spring data generated query you can use your handcrafted query by the @Query interface, one use case maybe you don’t want physical delete so you override the Spring Data query by your handcraft soft delete query.
Just redefine the method and add @Query interface and pass the Query.

@Query("select p from Person p where p.country like ?1")List<Person> findByCountryContains(String country);
Query DSL : Although when you search  entity by properties in Spring Data you just declare them in the interface but if you want to create a query which will search the entity dynamically. I mean to say your entity can be searched by the different combination of properties
what do you do?
One approach is declaring a method in all possible combination, but this is a bad idea your interface will be long and with addition and deletion of property combination need to be modified.

Criteria API is the Solution; we use Criteria API for the same reason.
We can introduce Predicate and Query Dsl for it

Query DSL: Spring data use  Query’s which is quite similar to criteria API but different approach. Just like the JPA Criteria API it uses a Java 6 annotation processor to generate meta-model objects but produces a much more approachable API.

To enable it
Add the following plugin in pom.xml

<plugin>
  <groupId>com.mysema.maven</groupId>
  <artifactId>maven-apt-plugin</artifactId>
  <version>1.0</version>
  <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>process</goal>
      </goals>
      <configuration>
        <outputDirectory>target/generated-sources</outputDirectory>
        <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
      </configuration>
    </execution>
  </executions>
</plugin>

In output Directory, it creates a special Class QPerson and using QPerson.person we can access every property and build our query.

Later I will discuss details on every bullet point with an example.
To follow it Please keep eye on javaonfly.blogspot.in


Benefits of Spring Data:
1.    Get rid from boiler plate Dao classes.
2.    Easily switch from one to another repository.
3.    You don’t have to an expert of JPA or Mongo Query Spring will take care of it in an optimized way.

4.    Highly flexible and support to add your custom query.