簡體   English   中英

返回列表<InvoiceDto>使用本機查詢從 Spring Boot 中的 SqlResultSetMapping ?

[英]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實體: ContollerServiceRepository 我不會編寫整個控制器層,只編寫調用服務層的方法,服務層@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]
  1. 嘗試按以下方式更正您的命名查詢:
@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"
)
  1. 檢查您的@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

  1. 你應該像這樣通過%
@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%運算符。 這讓我想起了PreparedStatementsJDBC編寫 SQL 查詢的方式,我在上學期在學校做的 :) 也許這是一個愚蠢的問題,但這在某種程度上是不是容易發生 SQL 注入?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM