Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

SpringData: Property Traversal

DZone's Guide to

SpringData: Property Traversal

In this article, Shamik Mitra will discuss the details of the "Queryname," derivation technique.

· Database Zone ·
Free Resource

RavenDB vs MongoDB: Which is Better? This White Paper compares the two leading NoSQL Document Databases on 9 features to find out which is the best solution for your next project.  

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

The 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) and a specific QureyTransalorFactory that will translate the method written in the custom repository to store a specific query.

Let's say that we have a Person Entity and a Person Repository. 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. Try to find an exact match for the stripped section and Person entity’s property. Because Person does not have any property called CountryContains, we go to Step 3.

  3. Again, query translator split the rest section based on camelCase pattern from the tail, so it now has two tokens (Country and Contains).

  4. Query translator tries to match Country with Person entity properties. 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, query translator understands the same and has put a like check.

  6. As underlying Store is JPA, now the query translator generates 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's check the Person bean again. We have a mapping between Person and Hobby; it is one to many relationships. Suppose we want to create a query that  will fetch Person based on a hobby. To do this, we just have to create a method in Person repository. Now, query translator tries to find a match for  “HobbyName”. As it is not matched, again it strips this phrase to two tokens (Hobby and Name). Then, it tries to match Hobby and it finds a List in Person entity. Then, it goes into the Hobby Entity and finds a property called Name, so it creates a query like the 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's 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. To overcome this situation, we can introduce a _(underscore) to demarcation the traversal path. The method name will be:

List<Person> findPersonByHobby_Name(String hobby);

Now QueryTranslator understands that Hobby and Name are two different tokens.

Get comfortable using NoSQL in a free, self-directed learning course provided by RavenDB. Learn to create fully-functional real-world programs on NoSQL Databases. Register today.

Topics:
spring 3.0 ,springdata ,spring data jpa ,java 5

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}