Close

Spring Data JPA - Nested Properties Resolution in Query Methods

[Updated: May 14, 2018, Created: May 14, 2018]

This example shows how to use nested entities' properties in query methods. we will also understand how nested properties of entities are resolved.

Example

Entities

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  @ManyToOne(cascade = CascadeType.ALL)
  private Dept dept;
  private int salary;
    .............
}
@Entity
public class Dept {
  @Id
  @GeneratedValue
  private Integer id;
  private String name;
    .............
}

Repository Interfaces

public interface EmployeeRepository extends CrudRepository<Employee, Long> {
  List<Employee> findByDept(Dept dept);
  List<Employee> findByDeptName(String deptName);
}

As seen above, we can query by employee.dept or by employee.dept.name

public interface DeptRepository extends CrudRepository<Dept, Integer> {
  Dept findByName(String name);
}

Example Client

@Component
public class ExampleClient {

  @Autowired
  private EmployeeRepository empRepo;
  @Autowired
  private DeptRepository deptRepo;


  public void run() {
      List<Employee> employees = createEmployees();
      empRepo.saveAll(employees);

      System.out.println(" -- finding by dept name IT --");
      List<Employee> list = empRepo.findByDeptName("IT");
      list.stream().forEach(System.out::println);

      System.out.println(" -- finding by dept entity Admin --");
      Dept admin = deptRepo.findByName("Admin");
      List<Employee> list2 = empRepo.findByDept(admin);
      list2.stream().forEach(System.out::println);
  }

  private List<Dept> createDepts() {
      return Arrays.asList(Dept.create("Admin"), Dept.create("IT"), Dept.create("Sale"));
  }

  private List<Employee> createEmployees() {
      Dept admin = Dept.create("Admin");
      Dept it = Dept.create("IT");
      Dept sale = Dept.create("Sale");
      return Arrays.asList(
              Employee.create("Diana", admin, 5000),
              Employee.create("Joe", it, 2000),
              Employee.create("Sophia", it, 6000),
              Employee.create("Mike", sale, 4000),
              Employee.create("Rose", it, 3000)
      );
  }
}

Main class

public class ExampleMain {

  public static void main(String[] args) {
      AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(AppConfig.class);
      ExampleClient exampleClient = context.getBean(ExampleClient.class);
      exampleClient.run();
      EntityManagerFactory emf = context.getBean(EntityManagerFactory.class);
      emf.close();
  }
}
 -- finding by dept name IT --
Employee{id=3, name='Joe', dept='Dept{id=4, name='IT'}', salary=2000}
Employee{id=5, name='Sophia', dept='Dept{id=4, name='IT'}', salary=6000}
Employee{id=8, name='Rose', dept='Dept{id=4, name='IT'}', salary=3000}
-- finding by dept entity Admin --
Employee{id=1, name='Diana', dept='Dept{id=2, name='Admin'}', salary=5000}

Nested Properties resolution

In query methods, properties are resolved by splitting them by camel case, but first a direct match is looked up in the target entity. In our example query method findByDeptName, first the query recognizing keyword 'findBy' is dropped, the remaining part 'DeptName' is looked into Employee entity. Since there's no property Employee.deptName exists, the query part is split into two: 'Dept' and 'Name'. Now the property Employee.dept.name is attempted. As it exists, the query method is resloved.

Now let's assume there's a direct property Employee.deptName exists along with Employee.dept.name. In that case, the direct property is given priority and is chosen to generate the JPA query instead of the nested property. Let's try that:

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  @ManyToOne(cascade = CascadeType.ALL)
  private Dept dept;
  private int salary;
  private String deptName;
    .............
}

Let's not populate any value for Employee.deptName and leave it null (otherwise it will still produce the same results by matching the correct dept name directly) and run our main method again:

-- finding by dept name IT --
-- finding by dept entity Admin --
Employee{id=1, name='Diana', dept='Dept{id=2, name='Admin'}', salary=5000}

As seen above the query empRepo.findByDeptName("IT") didn't return any rows because the direct property Employee.deptName is null (not IT).

Example Project

Dependencies and Technologies Used:

  • spring-data-jpa 2.0.6.RELEASE: Spring Data module for JPA repositories.
    Uses org.springframework:spring-context version 5.0.5.RELEASE
  • hibernate-core 5.2.13.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • h2 1.4.196: H2 Database Engine.
  • JDK 1.8
  • Maven 3.3.9

Nested Properties Example Select All Download
  • spring-data-jpa-nested-propreties
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also