Spring Data REST - How to include calculated data in a projection?
I have the following domain classes.
Credit 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;
}
Permission 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 credit class as follows:
@Projection(name = "studyLoanSingle", types = {Loan.class})
public interface LoanProjection {
String getLoanTitle();
List<AllowanceProjection> getAllowances();
}
Now I want to include the total loan amount (which is calculated by iterating through the list of permissions) in the projection and send it to the UI. Is it possible to do this in Spring Data REST?
source to share
From here :
You can annotate public properties in Projection with using
@Value
SpEL expressions to display synthetic properties. Even call methods on other Spring beans and pass a target to it for use in advanced computing .
So, you need to create a LoanRepo
bean method (for example) that will calculate the total amount of a 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 for example 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 the total amount:
GET http://localhost:8080/api/loans?projection=totalAmount
Everything looks fine, but we have a "minor" problem. For each record, as a result, we get an additional query to the database, which calculates the total amount. So you are faced with N + 1 questions here .
You can find my research on this issue in SDR using Projections here .
source to share
Putting domain objects on objects to solve the presentation of the representation (projection) is not the best solution.
A repository location would be useful for simple use cases, for complex problems where Java 8 will be present thanks default
to interface methods, 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;
}
}
source to share