DEV Community

loading...
Cover image for Spring Projections

Spring Projections

Shubham Kumar
Full-Stack Web Developer | On the quest to kill every bug out there.
・4 min read

If you have used Spring Data JPA, you must be knowing that all the query methods of Repository classes will return an entity object. There may be cases where we do not want an entire entity from the query method. We may be interested only in few attributes of that entity or subset of that entity with some manipulation in it. In those cases, we can use "Projection" which projects us only the required data out of entire entity class.

For example, lets say we have an entity class for Employee and we want to query our database based on this entity.

@Entity
@Table(name = "employee")
public class Employee implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Long id;

    @Column(name = "firstName")
    private String firstName;

    @Column(name = "lastName")
    private String lastName;

    @Column(name = "designation")
    private String designation;

    @Column(name = "salary")
    private Integer salary;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "departmentId", updatable = false, insertable = false)
    private Department department;

    @Column(name = "departmentId")
    private Long departmentId;

    public Long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getDesignation() {
        return designation;
    }

    public Integer getSalary() {
        return salary;
    }

    public Department getDepartment() {
        return department;
    }

    public Long getDepartmentId() {
        return departmentId;
    }
}
Enter fullscreen mode Exit fullscreen mode

And instead of returning the whole entity we only need Id, First Name, Last Name, and Department Name of Employee.

One way could be to use Data Transfer Object(DTO). The goal of a DTO class is to provide an efficient and strongly typed representation of the data returned by your query. To achieve that, a DTO class typically only defines a set of attributes, getter and setter methods for each of them, and a constructor that sets all attributes. But, this process can be simplifed by using spring projections.

There are two ways in which we can use projections while using Spring Data JPA:

  • Interface based
  • Class based

In this article, we will discuss Interface based projections.

Interface based projections

As the name suggests we will use an interface here. One can create an interface with only getter methods of properties we want from an entity class. This interface can then be the return type of query method we write in Spring Data JPA’s Repository interface.

Example 1 -

First we will create a interface with our desired properties:

public interface EmployeeProjectionOne {
    Long getId();

    String getFirstName();

    String getLastName();

    DepartmentDetails getDepartment();

    interface DepartmentDetails {
        String getName();
    }
}
Enter fullscreen mode Exit fullscreen mode

You can simply add this interface as a return type of query method in your Repository interface:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    List<EmployeeProjectionOne> findByFirstName(String firstName);
}
Enter fullscreen mode Exit fullscreen mode

This type of projection is called "Closed Projection" and in this case, the getter methods of interface match exactly with the getter methods of Entity’s properties.Also note that, recursive projections only work if we traverse from the owning side to the inverse side(Employee to Department). Were we to do it the other way around, the nested projection would be set to null.

Example 2 -

One can also use so called Open Projection,where we create interface with getter methods of selective properties only with Spring Expression Language(SpEL) expression. These projections enable us to define interface methods with unmatched names and with return values computed at runtime.
The SpEL expression will help us to define a new property from existing properties. Like in the above case if we want a property “fullName” then we can define it using SpEL expression and @Value annotation. Likewise, we can also define a new property from nested property of entity class.

public interface EmployeeProjectionTwo {
    Long getId();

    @Value("#{target.firstName} #{target.lastName}")
    String getFullName();

    @Value("#{target.department.name}")
    String getDepartmentName();
}
Enter fullscreen mode Exit fullscreen mode

And in the respository we can simply use:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    List<EmployeeProjectionTwo> findByLastName(String lastName);
}
Enter fullscreen mode Exit fullscreen mode

We can also define default methods in the projection-type interface to achieve the same thing, but it will just like writing boilerplate code.

Example 3

Now, based on your needs you can define 'n' number of projections for your entity class, but defining separate repository interfaces or methods for these multiple return types is too much of work.
To simplify this Spring data enables you to define what projection type to return from the query method at runtime dynamically. One will often come across this scenario while developing REST APIs so this is really a handy one.
Include a new argument to query method of type Class and set it with your projection type as follows:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

 List<T> findByFirstName(String firstName, Class<T> tClass);
}
Enter fullscreen mode Exit fullscreen mode

Then invoke the repository method from the service class:

@Override
public List<EmployeeProjectionTwo> getAllByDynamicProjection() {
    return employeeRepository.findByFirstName("A", EmployeeProjectionTwo.class);
}
Enter fullscreen mode Exit fullscreen mode

This type is called Dynamic projection because the attributes to return from the query method can be set during runtime.

These are the 3 types of Interface based projections that can be used in Spring Data JPA- Open, Closed and dynamic types. I will also be covering the class-based projects in a separate article, so if you liked this article stay tuned for part 2.

Discussion (0)