Spring 数据 REST - 如何在投影中包含计算数据?
Spring Data REST - How to include calculated data in a projection?
我定义了以下域 class。
贷款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;
}
津贴class
@Data
@Entity
public class Allowance {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private AllowanceType allowanceType;
private Double allowanceAmount;
}
我也有为贷款class定义的投影接口如下:
@Projection(name = "studyLoanSingle", types = {Loan.class})
public interface LoanProjection {
String getLoanTitle();
List<AllowanceProjection> getAllowances();
}
现在我想在投影中包含贷款总额(通过迭代津贴列表计算)并将其发送到 UI。是否可以在 Spring Data REST 中执行此操作?
来自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.
因此您必须创建一个 LoanRepo
bean 方法(例如)来计算给定贷款的总额:
@Query("select sum(a.allowanceAmount) as amount from Loan l join l.allowances a where l = ?1")
Double getTotalAmountByLoan(Loan loan);
并像这样使用投影:
@Projection(name = "totalAmount", types = Loan.class)
public interface LoanTotalAmount {
@Value("#{target}")
Loan getLoan();
@Value("#{@loanRepo.getTotalAmountByLoan(target)}")
Double getAmount();
}
那么您可以获得总金额的贷款:
GET http://localhost:8080/api/loans?projection=totalAmount
一切看起来都很好,但我们这里有一个 'small' 问题 - 对于结果中的每条记录,我们都会对计算总量的数据库进行额外查询。所以你在这里面对'N+1 queries issue`。
我对带有投影的 SDR 中的这个问题的调查你可以找到 here。
在域对象上放置方法来解决视图表示(投影)不是最佳解决方案。
Place on repository 对于简单的用例很有用,对于复杂的问题,由于 default
接口方法,Java 8 将出现,您可以使用这个简单的技巧。
@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;
}
}
我定义了以下域 class。
贷款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;
}
津贴class
@Data
@Entity
public class Allowance {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private AllowanceType allowanceType;
private Double allowanceAmount;
}
我也有为贷款class定义的投影接口如下:
@Projection(name = "studyLoanSingle", types = {Loan.class})
public interface LoanProjection {
String getLoanTitle();
List<AllowanceProjection> getAllowances();
}
现在我想在投影中包含贷款总额(通过迭代津贴列表计算)并将其发送到 UI。是否可以在 Spring Data REST 中执行此操作?
来自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.
因此您必须创建一个 LoanRepo
bean 方法(例如)来计算给定贷款的总额:
@Query("select sum(a.allowanceAmount) as amount from Loan l join l.allowances a where l = ?1")
Double getTotalAmountByLoan(Loan loan);
并像这样使用投影:
@Projection(name = "totalAmount", types = Loan.class)
public interface LoanTotalAmount {
@Value("#{target}")
Loan getLoan();
@Value("#{@loanRepo.getTotalAmountByLoan(target)}")
Double getAmount();
}
那么您可以获得总金额的贷款:
GET http://localhost:8080/api/loans?projection=totalAmount
一切看起来都很好,但我们这里有一个 'small' 问题 - 对于结果中的每条记录,我们都会对计算总量的数据库进行额外查询。所以你在这里面对'N+1 queries issue`。
我对带有投影的 SDR 中的这个问题的调查你可以找到 here。
在域对象上放置方法来解决视图表示(投影)不是最佳解决方案。
Place on repository 对于简单的用例很有用,对于复杂的问题,由于 default
接口方法,Java 8 将出现,您可以使用这个简单的技巧。
@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;
}
}