I have the following 3 JPA entities:
@Entity
public class Invoice {
private String number;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "invoice")
private List<Line> lines;
}
@Entity
public class Article {
private String code;
}
@Entity
public class Line {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "invoice_id")
private Invoice invoice;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "article_id")
private Article article;
}
I am trying to write a Spring Data projection to have the list of invoice numbers and its article codes, for which I created the following class:
@Data
@AllArgsConstructor
public class InvoiceWithArticlesView {
private String invoiceNumber;
private List<String> articleCodes;
}
This is one of my attempts which is not working as I am getting an error while the app starts up:
@Query("select new package.InvoiceWithArticlesView(i.number, l.article.code) from Invoice i join i.lines l where i.number in : invoiceNumbers")
List<InvoiceWithArticlesView> getInvoicesWithArticles(List<String> invoiceNumbers);
EDIT: This is the error I'm getting:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'invoiceRepository' defined in package.InvoiceRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List package.InvoiceRepository.getInvoicesWithArticles(java.util.List)! Reason: Validation failed for query for method public abstract java.util.List package.InvoiceRepository.getInvoicesWithArticles(java.util.List)!; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List package.InvoiceRepository.getInvoicesWithArticles(java.util.List)!
CodePudding user response:
I'm not sure if this answer will be good enough, but try next. Your query is good. Problem is in projection class (constructor).
First, add getters to all entity class, because projection class will use them.
Next change InvoiceWithArticlesView projection class in next way:
@Data
public class InvoiceWithArticlesView {
private String invoiceNumber;
private List<String> articleCodes = new ArrayList<>();
public InvoiceWithArticlesView(String invoiceNumber, Collection<Line> lines) {
this.invoiceNumber = invoiceNumber;
lines.forEach(line -> articleCodes.add(line.getArticle().getCode()));
}
}
and change repository method:
@Query("select new package.InvoiceWithArticlesView(i.number, i.lines) from Invoice i join i.lines l where i.number in :invoiceNumbers")
List<InvoiceWithArticlesView> getInvoicesWithArticles(@Param("invoiceNumbers") List<String> invoiceNumbers);
Because, query return list of lines, you must extract in some way. In this example, I sent List to ctr, iterate through them and added code to list articleCodes.
Also, you used named parameters, so you must add @Param annotation.