Close

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

[Last Updated: 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:

@Component
public class EmployeeInfoBean {

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

The projection interface:

public interface EmployeeInfo {
    @Value("#{@employeeInfoBean.getDisplayString(target)}")
    String getDisplayString();
}

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

Complete Example

Entities

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

The Bean

@Component
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(),
                employee.getDepartment().getLocation());
    }
}

Projection interface

public interface EmployeeInfo {
    @Value("#{@employeeInfoBean.getDisplayString(target)}")
    String getDisplayString();

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

Example client

@Component
public class ExampleClient {

    @Autowired
    private EmployeeRepository repo;

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

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

        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");
            System.out.println(str);
        });
    }

    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);
        exampleClient.run();
        EntityManagerFactory emf = context.getBean(EntityManagerFactory.class);
        emf.close();
    }
}
-- 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
                • EmployeeInfo.java
          • resources
            • META-INF

    See Also