Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data REST - How to include calculated data in a projection?

I have the following domain classes defined.

Loan Class

@Data
@Entity
public class Loan {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String loanTitle;


    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "loan_id")
    private List<Allowance> allowances;
}

Allowance class

@Data
@Entity
public class Allowance {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    private AllowanceType allowanceType;


    private Double allowanceAmount;

}

I also have a projection interface defined for the loan class as follows:

@Projection(name = "studyLoanSingle", types = {Loan.class})
public interface LoanProjection {

    String getLoanTitle();

    List<AllowanceProjection> getAllowances();

}

Now I want to include the total amount of the loan(which is calculated by iterating the list of Allowances) in the projection and send it to the UI. Is it possible to do this in Spring Data REST?

like image 474
Charlie Avatar asked Jul 23 '17 02:07

Charlie


People also ask

What do spring data rest projections do?

Spring data REST Projection supports projecting only a selected fields from an entity representation. To do that, we can define those specific fields into our @Projection interface. Let's create a custom view of our Student entity with first name and last name fields.

What does the @RepositoryRestResource annotation do?

@RepositoryRestResource is used to set options on the public Repository interface - it will automatically create endpoints as appropriate based on the type of Repository that is being extended (i.e. CrudRepository/PagingAndSortingRepository/etc).

What is projection in REST API?

What Are Projections? It is a common practice to use Domain Transfer Objects in REST API design as a method of separating the API from its underlying model. This is particularly relevant to Spring Data JPA REST where you may want to restrict what is visible to clients.


2 Answers

From here:

You can annotate exposed properties in Projection with @Value using SpEL expressions to expose synthetic properties. Even invoke methods on other Spring beans and hand over the target to it for use in advanced calculations.

So you have to create a LoanRepo bean method (for example) that calculate the total amount of the given loan:

@Query("select sum(a.allowanceAmount) as amount from Loan l join l.allowances a where l = ?1")
Double getTotalAmountByLoan(Loan loan);

and use like this Projection:

@Projection(name = "totalAmount", types = Loan.class)
public interface LoanTotalAmount {

    @Value("#{target}")
    Loan getLoan();

    @Value("#{@loanRepo.getTotalAmountByLoan(target)}")    
    Double getAmount();
}

Then you can get your loans with total amount:

GET http://localhost:8080/api/loans?projection=totalAmount

All looks fine but we have a 'small' issue here - for each record in the result we get an extra query to the DB that calculate total amount. So you faces here with 'N+1 queries issue`.

My investigation of this problem in SDR with Projections you can find here.

like image 137
Cepr0 Avatar answered Nov 15 '22 07:11

Cepr0


Place on domain objects methods to solve a view representation (projection) is not the best solution.

Place on repository would be useful for simple use cases, for complex issues where Java 8 will be present thanks to default methods of interface you can use this simple trick.

@Projection(name = "studyLoanSingle", types = Loan.class)
public interface LoanProjection {

    String getLoanTitle();

    //If no need Allowances on json
    @JsonIgnore
    List<Allowance> getAllowances();

    public default Double getAmount() {
        Double result = new Double(0);
        for (Allowance a : getAllowances()) {
           result += a.getAllowanceAmount();            
        }
        return result;
    }
}
like image 37
pdorgambide Avatar answered Nov 15 '22 09:11

pdorgambide