Spring boot data jpa-Query for nested object

Nowadays spring boot and spring data are widely used frameworks. If you are building web application in java, spring boot is the first choice. If you have Database then spring data JPA is best choice. In spring boot spring data is the default implementation for database interaction. Spring boot using spring data JPA even creates nested object query from method name.

Therefore, if you are using spring boot or spring data JPA, you must know how queries are created specially if you have nested object structure. 

Introduction

Spring data JPA provides repository abstraction and reduces the boiler plate code from persistence layers. As discussed in our previous blog Spring data Java beginner, we can define queries in two ways, Query annotation and Method name itself.

For simple use cases method name for query resolution is best option. However, using spring boot for nested object property without knowing how it works, could cause big issue at runtime.

Lets understand what could be the problem in nested object query and how can we resolve it.

Problem statement

For example if you create method findByDepartmentId, what will be the query? It would be either,

Select * from ClassRoom where departmentId=?

OR

Select * from ClassRoom c left join department d on <condition> where d.id =?

Both are the possibilities depending on the class structure like

Class ClassRoom {
   Long departmentId;
}

Or

Class ClassRoom {
    Department department;
}
Class Department {
    Long id;
}

Then the big question is, what happens when we have following structure?

Class ClassRoom {
    Department department;
    Long departmentId;
}

Confussed face
Class ClassRoom {
    Department department;
    Long departmentId;
}

How spring data resolves method name

Let's see how spring data jpa in spring boot decides what join should be used to avoid wrong select query.

Consider the object structure as below for these classes : Student- classroom-department

Class Student {
    ...
    ClassRoom classRoom;
}
Class ClassRoom {
    …..
    Department department;
}
Class Department{
    ...
    Long id;
}

We will be using above database structure and try to understand what happens when we create a method findByClassRoomDepartmentId(Long id)

Following are the steps that spring data jpa will perform to create query

  • Start by taking complete name as classRoomDepartmentId under the domain class
  • If this doesnot match split word at camel case part form right side into 2 parts as classRoomDepartment and id
    • Check whether first property matches, take that property and continue for second part- keep building tree
    • If first side does not match, continue to split to next point i.e. classRoom and departmentId
How spring boot resolve nested object query from method name using spring data JPA
Method name to property resolution flow diagram

In most of the cases this works, however this could fail if we have Long classRoomDepartment variable in Student class. JPA will find the first match as classRoomDepartment, it will select and fail because it will try to find id variable inside the classRoomDepartment which is Long class. But as there is no id property inside the Long class and it will fail.

Solution:

To solve such problem, we can use '_' (Underscore) character inside the method name to define where JPA should try to split. In this case our method name will be findByClassRoom_DepartmentId()

Using underscore is not in the java naming best practice, but this is allowed in spring data JPA.

Fast track reading

  • In Spring data Java queries can be defined using Query annotation or Method name
  • For simple cases derived query from method name is best choice
  • We can use method name to derive query for nested object also
  • Spring data jpa starts checking for property from full name and starts breaking at camel case from right to left
  • To avoid the property name confusion ‘_’ (Underscore) character can be used to separate property names

Related topics

Leave a Reply

Your email address will not be published. Required fields are marked *