Spring Data JPA - Invoking Bean Methods from Projections' SpEL expressions

[Updated: Aug 29, 2018, Created: Aug 29, 2018]

In the last tutorial we saw how to use Java 8 methods in projection interface to apply custom query logic. Another more flexible option is to implement custom logic in a Spring bean and invoke the bean methods from the projections' SpEL expressions.

Quick Example

The bean:

public class EmployeeInfoBean {

    public String getDisplayString(Employee employee) {
        //apply custom logic here

The projection interface:

public interface EmployeeInfo {
    String getDisplayString();

As seen above, the symbol @ is used to access the bean reference in SpEL expression.

Complete Example


public class Employee {
  private Integer id;
  private String name;
  @ManyToOne(cascade = CascadeType.ALL)
  private Department department;
  private int salary;
public class Department {
  private Integer id;
  private String deptName;
  private String location;

The Bean

public class EmployeeInfoBean {

  public String getDisplayString(Employee employee) {
      return String.format("%s (%s)", employee.getName(), employee.getDepartment().getDeptName());

  public String getDetailedString(Employee employee, String labelName, String labelDept) {
      return String.format("%s: %s, %s: %s, %s",
              labelName, employee.getName(),
              labelDept, employee.getDepartment().getDeptName(),

Projection interface

public interface EmployeeInfo {
  String getDisplayString();

  @Value("#{@employeeInfoBean.getDetailedString(target, args[0], args[1])}")
  String getDetailedString(String labelName, String labelDept);

Example client

public class ExampleClient {

  private EmployeeRepository repo;

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

      System.out.println("-- finding all employees --");
      Iterable<Employee> all = repo.findAll();

      System.out.println("-- All EmployeeInfo --");
      List<EmployeeInfo> list = repo.findBy();

      System.out.println("-- display strings --");
      list.forEach(employeeInfo -> System.out.println(employeeInfo.getDisplayString()));

      System.out.println("-- detailed display strings --");
      list.forEach(employeeInfo -> {
          String str = employeeInfo.getDetailedString("Employee", "Department");

  private List<Employee> createEmployees() {
      return Arrays.asList(
              Employee.of("Diana", Department.of("Admin", "NY"), 3000),
              Employee.of("Mike", Department.of("IT", "TX"), 35000),
              Employee.of("Rose", Department.of("Sales", "NC"), 4000),
              Employee.of("Sara", Department.of("Admin", "TX"), 3500),
              Employee.of("Joe", Department.of("IT", "TX"), 3000),
              Employee.of("Charlie", Department.of("IT", "NY"), 4500)

Main class

public class ExampleMain {

  public static void main(String[] args) {
      AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(AppConfig.class);
      ExampleClient exampleClient = context.getBean(ExampleClient.class);;
      EntityManagerFactory emf = context.getBean(EntityManagerFactory.class);
-- finding all employees --
Employee{id=1, name='Diana', department=Department{id=2, deptName='Admin', location='NY'}}
Employee{id=3, name='Mike', department=Department{id=4, deptName='IT', location='TX'}}
Employee{id=5, name='Rose', department=Department{id=6, deptName='Sales', location='NC'}}
Employee{id=7, name='Sara', department=Department{id=8, deptName='Admin', location='TX'}}
Employee{id=9, name='Joe', department=Department{id=10, deptName='IT', location='TX'}}
Employee{id=11, name='Charlie', department=Department{id=12, deptName='IT', location='NY'}}
-- All EmployeeInfo --
-- display strings --
Diana (Admin)
Mike (IT)
Rose (Sales)
Sara (Admin)
Joe (IT)
Charlie (IT)
-- detailed display strings --
Employee: Diana, Department: Admin, NY
Employee: Mike, Department: IT, TX
Employee: Rose, Department: Sales, NC
Employee: Sara, Department: Admin, TX
Employee: Joe, Department: IT, TX
Employee: Charlie, Department: IT, NY

Example Project

Dependencies and Technologies Used:

  • spring-data-jpa 2.0.9.RELEASE: Spring Data module for JPA repositories.
    Uses org.springframework:spring-context version 5.0.8.RELEASE
  • hibernate-core 5.3.5.Final: Hibernate's core ORM functionality.
    Implements javax.persistence:javax.persistence-api version 2.2
  • h2 1.4.197: H2 Database Engine.
  • JDK 1.8
  • Maven 3.5.4

Projections SpEL expression Invoking Bean Methods Select All Download
  • spring-data-jpa-projection-expression-calling-beans
    • src
      • main
        • java
          • com
            • logicbig
              • example
          • resources
            • META-INF

    See Also