![](/img/trans.png)
[英]Native query is returning all the records for the #pageable in spring boot
[英]Returning List<InvoiceDto> from SqlResultSetMapping in Spring Boot using native query?
所以,我想提取物清單DTO
期從實體表,而不必首先獲得所有實體(這是滿足一些條件),然后使DTO
出去了實體。 這是我現在所擁有的。
具有以下sql mappings
Invoice
實體:
@Entity
@SqlResultSetMappings({
@SqlResultSetMapping(name = "findInvoicesDtoMapping",
classes = {@ConstructorResult(targetClass=com.path.to.dto.invoice.InvoiceDto.class,
columns = {@ColumnResult(name="invoiceId", type=Integer.class),
@ColumnResult(name="projectNumber", type=String.class),
})} ),
})
@NamedNativeQueries(value = {
@NamedNativeQuery(name = "findInvoicesDto", query = ""
+"SELECT invoice.invoice_id AS invoiceId, invoice.project_number AS projectNumber "
+"FROM Invoiceinvoice "
+"WHERE invoice_tour.paid = :paid OR invoice_tour.invoice_number LIKE % :invoiceNumber",
resultSetMapping = "findInvoicesTourMapping"),
})
@Table(name = "invoice_tour")
public class InvoiceTour {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "invoice_id")
private int invoiceId;
@Column(name = "date_of_finishing", unique = false)
private String dateOfFinishing;
@Column(name = "account_number", unique = false)
private String accountNumber;
@Column(name = "project_number", unique = false)
private String projectNumber;
@Column(name = "invoice_number", unique = false)
private String invoiceNumber;
@Column(name = "paid", unique = false)
private boolean paid;
//omitted gettes&setters
}
這是DTO
類:
public class InvoiceDto {
private String projectNumber;
private int invoiceId;
public(int invoiceId, String projectNumber){
this.invoiceId = invoiceId;
this.projectNumber = projectNumber;
}
//omitted gettes&setters
}
我有我的三層InvoiceTour
實體: Contoller
, Service
和Repository
。 我不會編寫整個控制器層,只編寫調用服務層的方法,服務層@NamedNativeQuery
調用存儲庫層和最終存儲庫層方法,該方法引用@NamedNativeQuery
。
public interface InvoiceTourService {
List<InvoiceDto> findInvoices(String invoiceNumber, boolean paid);
}
InvoiceTour
Service
:
@Service
@Validated
public class InvoiceTourServiceImpl implements InvoiceTourService{
private final InvoiceTourRepository repository;
@Inject
public InvoiceTourServiceImpl(final InvoiceTourRepository repository) {
this.repository = repository;
}
@Override
public List<InvoiceDto> findInvoices(String invoiceNumber, boolean paid) {
return repository.findInvoices(invoiceNumber, paid);
}
}
InvoiceTour
Repository
:
@Repository
public interface InvoiceTourRepository extends JpaRepository<InvoiceTour, Integer>{
@Query(name = "findInvoicesDto", nativeQuery = true)
List<InvoiceDto> findInvoices(@Param("invoiceNumber") String invoiceNumber, @Param("paid") boolean paid);
}
這會產生以下異常:
could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
有沒有辦法使用@SqlResultSetMappings
提取DTO
自定義POJO
的列表並直接形成DTO
的集合?
更新 1好吧,我測試了另外兩種情況:
情況1:
在WHERE
子句中,我在%
運算符和:invoiceNumber
之間放置了一個空格,如下所示:
LIKE % :invoiceNumber %
在那種情況下,我得到了:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'invoice_tour.invoice_number LIKE % 'a' % OR invoice_tour.company_id = 2' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_261]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_261]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_261]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_261]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) ~[mysql-connector-java-5.1.46.jar:5.1.46]
at com.mysql.jdbc.Util.getInstance(Util.java:408) ~[mysql-connector-java-5.1.46.jar:5.1.46]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:944) ~[mysql-connector-java-5.1.46.jar:5.1.46]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3976) ~[mysql-connector-java-5.1.46.jar:5.1.46]
案例2:
在WHERE
子句中,我在%
運算符和:invoiceNumber
之間沒有放置空格,如下所示:
LIKE %:invoiceNumber%
在那種情況下,我得到了:
org.springframework.dao.InvalidDataAccessApiUsageException: Unknown parameter name : invoiceNumber; nested exception is java.lang.IllegalArgumentException: Unknown parameter name : invoiceNumber
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:367) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateI
fNecessary(DataAccessUtils.java:242) ~[spring-tx-5.0.8.RELEASE.jar:5.0.8.RELEASE]
更新 2
好吧,我又測試了一次,完全使用UPDATE 1之前的代碼。
在這種情況下,WHERE 子句中的代碼如下:
LIKE % :invoiceNumber %
我還在控制台中打開了 od sql 語句生成,這就是我所擁有的:
SELECT invoice_tour.invoice_id AS invoiceId, invoice_tour.project_number AS projectNumber
FROM Invoice_tour invoice_tour WHERE invoice_tour.paid = ? OR invoice_tour.invoice_number LIKE % ? %
我得到的例外:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-5.0.8.RELEASE.jar:5.0.8.RELEASE]
@NamedNativeQuery(
name = "findInvoicesDto",
query =
"SELECT " +
" invoice.invoice_id AS invoiceId, " +
" invoice.project_number AS projectNumber " +
"FROM invoice_tour " +
"WHERE invoice_tour.paid = :paid OR invoice_tour.invoice_number LIKE :invoiceNumber",
resultSetMapping = "findInvoicesTourMapping"
)
@SqlResultSetMapping
。 應該是這樣的:@SqlResultSetMapping(name = "findInvoicesTourMapping",
classes = @ConstructorResult(
targetClass=com.path.to.dto.invoice.InvoiceDto.class,
columns = {@ColumnResult(name="invoiceId"),
@ColumnResult(name="projectNumber"),
}
)
)
注意@SqlResultSetMapping.name
等於@NamedNativeQuery.resultSetMapping
。
%
:@Service
@Validated
public class InvoiceTourServiceImpl implements InvoiceTourService{
private final InvoiceTourRepository repository;
// ...
@Override
public List<InvoiceDto> findInvoices(String invoiceNumber, boolean paid) {
return repository.findInvoices("%" + invoiceNumber + "%", paid);
}
}
為什么這里直接使用SQL? JPQL/HQL 完全有能力表示這種查詢。 通過避免使用 SQL,您可以從 Spring Data JPA 提供的一些抽象中受益,即您可以刪除實體上的所有命名本機查詢注釋,而是在存儲庫中使用以下內容:
@Query("FROM InvoiceTour t WHERE t.paid = :paid OR t.invoiceNumber LIKE '%' || :invoiceNumber")
List<InvoiceDto> findInvoices(@Param("invoiceNumber") String invoiceNumber, @Param("paid") boolean paid);
您可能還喜歡Blaze-Persistence Entity Views提供的功能。
我創建了該庫以允許在 JPA 模型和自定義接口或抽象類定義的模型之間輕松映射,例如類固醇上的 Spring Data Projections。 這個想法是您按照自己喜歡的方式定義目標結構(域模型),並通過 JPQL 表達式將屬性(getter)映射到實體模型。
使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:
@EntityView(InvoiceTour.class)
public interface InvoiceDto {
@IdMapping
Integer getInvoiceId();
String getProjectNumber();
}
查詢是將實體視圖應用於查詢的問題,最簡單的只是按 id 查詢。
InvoiceDto a = entityViewManager.find(entityManager, InvoiceDto.class, id);
Spring Data 集成允許您幾乎像 Spring Data Projections 一樣使用它: https : //persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
您可以使用 Spring Data 命名約定來構建大致如下所示的查詢:
List<InvoiceDto> findByInvoiceNumberOrPaid(@Param("invoiceNumber") String invoiceNumber, @Param("paid") boolean paid);
所以,總結一下。 由於 SternK 的回答和文檔,我能夠得到想要的結果。 所以以下兩種方法給了我正確的結果:
方法一:
List<InvoiceDto> invoices = em.createNamedQuery("findInvoicesDto" )
.setParameter("invoiceNumber", "%" + invoiceNumber + "%")
.setParameter("paid", paid)
.getResultList();
字符串findInvoicesDto
必須匹配@SqlResultSetMapping.name
。
方法二:
就像在 SternK 的回答中一樣。
注意:我沒想到這種語法在 Hibernate 中帶有LIKE
和%
運算符。 這讓我想起了PreparedStatements
和JDBC
編寫 SQL 查詢的方式,我在上學期在學校做的 :) 也許這是一個愚蠢的問題,但這在某種程度上是不是容易發生 SQL 注入?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.