Dữ liệu mùa xuân và Truy vấn gốc có phân trang


85

Trong một dự án web, sử dụng dữ liệu mùa xuân mới nhất (1.10.2) với cơ sở dữ liệu MySQL 5.6, tôi đang cố gắng sử dụng truy vấn gốc với phân trang nhưng tôi gặp phải lỗi org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodExceptionkhi khởi động.

CẬP NHẬT : 20180306 Sự cố này hiện đã được khắc phục trong Spring 2.0.4 Đối với những người vẫn quan tâm hoặc gặp khó khăn với các phiên bản cũ hơn, hãy kiểm tra các câu trả lời và nhận xét liên quan để biết cách giải quyết.

Theo Ví dụ 50 tại Sử dụng @Query từ tài liệu spring-data, có thể chỉ định chính truy vấn và countQuery, như sau:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

Vì tò mò, Trong NativeJpaQuerylớp, tôi có thể thấy rằng nó chứa đoạn mã sau để kiểm tra xem đó có phải là một truy vấn jpa hợp lệ hay không:

public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
   super(method, em, queryString, evaluationContextProvider, parser);
   JpaParameters parameters = method.getParameters();
   boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() || parameters.hasSortParameter();
   boolean containsPageableOrSortInQueryExpression = queryString.contains("#pageable") || queryString.contains("#sort");
   if(hasPagingOrSortingParameter && !containsPageableOrSortInQueryExpression) {
       throw new InvalidJpaQueryMethodException("Cannot use native queries with dynamic sorting and/or pagination in method " + method);
   }
}

Truy vấn của tôi có chứa một Pageabletham số, vì vậy hasPagingOrSortingParametertrue, nhưng nó cũng đang tìm kiếm một #pageablehoặc #sortchuỗi bên trong queryString, mà tôi không cung cấp.

Tôi đã thử thêm #pageable(đó là một nhận xét) vào cuối truy vấn của mình, điều này làm cho quá trình xác thực được thông qua nhưng sau đó, nó không thực hiện được khi nói rằng truy vấn cần một tham số bổ sung: 3 thay vì 2.

Điều buồn cười là, nếu tôi tự thay đổi containsPageableOrSortInQueryExpressiontừ falseđể truekhi chạy, truy vấn hoạt động tốt vì vậy tôi không biết tại sao nó kiểm tra các chuỗi có mặt tại của tôi queryStringvà tôi không biết làm thế nào để cung cấp nó.

Bất kì sự trợ giúp nào đều được đánh giá cao.

Cập nhật ngày 30/01/2018 Có vẻ như các nhà phát triển tại dự án dữ liệu mùa xuân đang làm việc để khắc phục sự cố này với PR của Jens Schauder


Không, tôi vẫn chưa tìm ra giải pháp. Tôi đã tạo một vé trong Spring JIRA nhưng không có phản hồi: jira.spring.io/browse/DATAJPA-928 . Cuối cùng, tôi không cần phân trang nên tôi đã không cố gắng điều tra thêm hoặc đẩy mạnh hơn với tấm vé đó.
Lasneyx

3
Được rồi, cảm ơn. Để giải quyết vấn đề, bạn có thể đã thêm "\ n # pagable \ n" thay vì "#pagable", nhưng tôi hy vọng nó sẽ được sửa trong tương lai.
Janar

1
bản sửa lỗi hiện tại khiến mọi thứ trở nên tồi tệ hơn đối với tôi. bây giờ tham số PageAble của tôi hoàn toàn bị bỏ qua và truy vấn được thực thi không giới hạn. vì vậy, nếu bạn đã chèn giải pháp thay thế \ n # pagable \ n đó, hãy nhớ xóa nó đi, nếu không bạn sẽ gặp rắc rối.
Rüdiger Schulz

Câu trả lời:


48

Tôi xin lỗi trước, điều này tổng hợp khá nhiều câu hỏi ban đầu và nhận xét từ Janar , tuy nhiên ...

Tôi gặp phải vấn đề tương tự: Tôi tìm thấy Ví dụ 50 của Dữ liệu Spring là giải pháp cho nhu cầu của tôi về việc có một truy vấn gốc với phân trang nhưng Spring đã phàn nàn khi khởi động rằng tôi không thể sử dụng phân trang với các truy vấn gốc.

Tôi chỉ muốn báo cáo rằng tôi đã quản lý để chạy thành công truy vấn gốc mà tôi cần, bằng cách sử dụng phân trang, với mã sau:

    @Query(value="SELECT a.* "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time)"
            + "ORDER BY a.id \n#pageable\n", 
        /*countQuery="SELECT count(a.*) "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time) \n#pageable\n",*/
        nativeQuery=true)
public List<Author> findAuthorsUpdatedAndNew(Pageable pageable);

CountQuery (được nhận xét trong khối mã) là cần thiết để sử dụng Page<Author> làm loại trả về của truy vấn, các dòng mới xung quanh nhận xét "#pagable" là cần thiết để tránh lỗi thời gian chạy về số lượng tham số dự kiến ​​(cách giải quyết của cách giải quyết). Tôi hy vọng lỗi này sẽ sớm được sửa ...


1
Trong trường hợp của tôi ?#{#pageable}là làm việc thay thế \n#pageable\n.
Dmitry Stolbov

6
Những công việc này. Đối với các đối số, bạn vẫn có thể sử dụng các tham số được đặt tên. ví dụ :authorId. Tôi đã đổi của bạn \n#pageable\nthành --#pageable\ncho postgresql. CountQuery là cần thiết như bạn đã đề xuất vì nhiều ví dụ sử dụng Danh sách <> thay vì Trang <> câu trả lời của bạn đã được đưa ra cho điều này. Tôi đã xem qua các tài liệu tương tự và nếu không có câu trả lời của bạn, tôi sẽ bỏ cuộc.
Abhishek Dujari

2
Nếu bạn không viết countQuery và để khuôn khổ xử lý điều đó, đôi khi không ghi số đếm theo truy vấn một cách chính xác cho bạn và bạn có thể thấy lỗi là danh sách trường không hợp lệ. Bạn luôn có thể phân tích các câu lệnh SQL bằng cách thêm các mục nhập bên dưới vào tệp thuộc tính logging.level.org.hibernate.SQL = DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder = TRACE Thêm số đếm theo truy vấn riêng biệt sẽ khắc phục sự cố. Điều này có thể phụ thuộc vào các phiên bản khung và cơ sở dữ liệu bạn đang sử dụng.
Nalaka

Trong trường hợp của tôi chỉ /*:pageable*/hoạt động (với SQL Server, Hibernate 5.4.1.Final và Spring Boot 2.2.1)
marioosh

35

Đây là một bản hack cho chương trình sử dụng Spring Data JPA trước Phiên bản 2.0.4 .

Mã đã hoạt động với PostgreSQL và MySQL:

public interface UserRepository extends JpaRepository<User, Long> {

@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY ?#{#pageable}",
       countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
       nativeQuery = true)
   Page<User> findByLastname(String lastname, Pageable pageable);   
}

ORDER BY ?#{#pageable}là dành cho Pageable. countQuerylà dành cho Page<User>.


1
Tôi không biết. Hãy thử 1. cho phép ghi nhật ký các câu lệnh SQL - đặt spring.jpa.show-sql=falsetrong application.properties; 2. đặt ?#{#pageable}trong nativeQuery của bạn và 3. phân tích kết quả SQL từ nhật ký. @Lasneyx đã tạo vấn đề DATAJPA-928 cho đặc thù này của Spring Data JPA.
Dmitry Stolbov

12

Chỉ dành cho bản ghi, sử dụng H2 làm cơ sở dữ liệu thử nghiệm và MySQL trong thời gian chạy, phương pháp này hoạt động (ví dụ là đối tượng mới nhất trong nhóm ):

@Query(value = "SELECT t.* FROM t LEFT JOIN t AS t_newer " +
        "ON t.object_id = t_newer.object_id AND t.id < t_newer.id AND o_newer.user_id IN (:user_ids) " +
        "WHERE t_newer.id IS NULL AND t.user_id IN (:user_ids) " +
        "ORDER BY t.id DESC \n-- #pageable\n",
        countQuery = "SELECT COUNT(1) FROM t WHERE t.user_id IN (:user_ids) GROUP BY t.object_id, t.user_id",
        nativeQuery = true)
Page<T> findByUserIdInGroupByObjectId(@Param("user_ids") Set<Integer> userIds, Pageable pageable);

Dữ liệu mùa xuân JPA 1.10.5, H2 1.4.194, Máy chủ cộng đồng MySQL 5.7.11-log (innodb_version 5.7.11).


Làm việc cho PostgreSQL. Cảm ơn bạn!
Maksym Chernikov

điều gì sẽ xảy ra nếu chúng ta phải chuyển thứ tự bằng asc hoặc desc động cho truy vấn?
Kulbhushan Singh,

8

Tôi có cùng một triệu chứng giống như @Lasneyx. Giải pháp của tôi cho truy vấn gốc Postgres

@Query(value = "select * from users where user_type in (:userTypes) and user_context='abc'--#pageable\n", nativeQuery = true)
List<User> getUsersByTypes(@Param("userTypes") List<String> userTypes, Pageable pageable);

Cũng làm việc cho tôi với DB2.
Anders Metnik

@Query (nativeQuery = true, value = "select a. * From rqst a LEFT OUTER JOIN table_b b ON a.id = b.id trong đó a.code = 'abc' ORDER BY - # pagable \ n") thì không đang hoạt động: Không thể xác định loại dữ liệu của tham số $ 2
Nhà phát triển

7

Thử cái này:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY /*#pageable*/",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

( "/* */"cho Oracle notation)


một số lý do genreated truy vấn có dấu phẩy sau pageable một số điều như thế này "ORDER BY / * # pageable * /", gây ra lỗi cú pháp
d-người đàn ông

tôi xóa đơn đặt hàng nhưng đã tạo ra ngoại lệ không mong muốn char: '#'
hicham abdedaime

5

Tôi sử dụng cơ sở dữ liệu oracle và tôi không nhận được kết quả nhưng lỗi với dấu phẩy được tạo mà d-man nói ở trên.

Sau đó, giải pháp của tôi là:

Pageable pageable = new PageRequest(current, rowCount);

Như bạn có thể thấy mà không cần thứ tự khi tạo Pagable.

Và phương pháp trong DAO:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 /*#pageable*/ ORDER BY LASTNAME",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
 }

1

Cả hai cách tiếp cận sau đều hoạt động tốt với MySQL để phân trang truy vấn gốc. Tuy nhiên, chúng không hoạt động với H2. Nó sẽ khiếu nại lỗi cú pháp sql.

  • ĐẶT HÀNG BẰNG? # {# Pagable}
  • ĐẶT HÀNG BẰNG a.id \ n # có thể đánh số trang \ n

1

Sử dụng "ORDER BY id DESC \ n-- #pagable \ n" thay vì "ORDER BY id \ n # pagrable \ n" phù hợp với tôi với MS SQL SERVER


1

Tôi có thể tích hợp Pagination thành công trong

Spring-data-jpa-2.1.6

như sau.

@Query(
 value = “SELECT * FROM Users”, 
 countQuery = “SELECT count(*) FROM Users”, 
 nativeQuery = true)
Page<User> findAllUsersWithPagination(Pageable pageable);

0

Nó hoạt động như sau:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "select * from (select (@rowid\\:=@rowid+1) as RN, u.* from USERS u, (SELECT @rowid\\:=0) as init where  LASTNAME = ?1) as total"+
        "where RN between ?#{#pageable.offset-1} and ?#{#pageable.offset + #pageable.pageSize}",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
    Page<User> findByLastname(String lastname, Pageable pageable);
}

0

Đối với tôi bên dưới đã làm việc trong MS SQL

 @Query(value="SELECT * FROM ABC r where r.type in :type  ORDER BY RAND() \n-- #pageable\n ",nativeQuery = true)
List<ABC> findByBinUseFAndRgtnType(@Param("type") List<Byte>type,Pageable pageable);

0

Tôi đang sử dụng mã bên dưới. đang làm việc

@Query(value = "select * from user usr" +
  "left join apl apl on usr.user_id = apl.id" +
  "left join lang on lang.role_id = usr.role_id" +
  "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds ORDER BY ?#{#pageable}",
  countQuery = "select count(*) from user usr" +
      "left join apl apl on usr.user_id = apl.id" +
      "left join lang on lang.role_id = usr.role_id" +
      "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds",
  nativeQuery = true)
Page<AplUserEntity> searchUser(@Param("scrname") String scrname,@Param("uname") String  uname,@Param("roleIds") List<Long> roleIds,Pageable pageable);

1
Vui lòng giải thích tại sao câu trả lời này tốt hơn bất kỳ câu trả lời nào khác đã tồn tại trong vài năm?
Dragonthoughts

0

Việc xóa \ n # có thể phân trang \ n khỏi cả truy vấn và truy vấn đếm đều phù hợp với tôi. Phiên bản Springboot: 2.1.5. RELEASE DB: Mysql


0

Điều này đã làm việc cho tôi (tôi đang sử dụng Postgres) ở Groovy:

@RestResource(path="namespaceAndNameAndRawStateContainsMostRecentVersion", rel="namespaceAndNameAndRawStateContainsMostRecentVersion")
    @Query(nativeQuery=true,
            countQuery="""
            SELECT COUNT(1) 
            FROM 
            (
                SELECT
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))
            ) t
            WHERE version_partition = 1
            """,
            value="""
            SELECT id, version, state, name, internal_name, namespace, provider_id, config, create_date, update_date 
            FROM 
            (
                SELECT 
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE 
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))       
            ) t            
            WHERE version_partition = 1             
            /*#{#pageable}*/
            """)
    public Page<Entity> findByNamespaceContainsAndNameContainsAndRawStateContainsMostRecentVersion(@Param("namespace")String namespace, @Param("name")String name, @Param("state")String state, Pageable pageable)

Chìa khóa ở đây là sử dụng: /*#{#pageable}*/

Nó cho phép tôi sắp xếp và phân trang. Bạn có thể kiểm tra nó bằng cách sử dụng một cái gì đó như sau: http: // localhost: 8080 / api / v1 / entity / search / namespaceAndNameAndRawStateContainsMostRecentVersion? Namespace = & name = & state = Published & page = 0 & size = 3 & sort = name, desc

Chú ý vấn đề này: Spring Pagable không dịch tên @Column


0

Bạn có thể sử dụng mã dưới đây cho h2 và MySQl

    @Query(value = "SELECT req.CREATED_AT createdAt, req.CREATED_BY createdBy,req.APP_ID appId,req.NOTE_ID noteId,req.MODEL model FROM SUMBITED_REQUESTS  req inner join NOTE note where req.NOTE_ID=note.ID and note.CREATED_BY= :userId "
        ,
         countQuery = "SELECT count(*) FROM SUMBITED_REQUESTS req inner join NOTE note WHERE req.NOTE_ID=note.ID and note.CREATED_BY=:userId",
        nativeQuery = true)
Page<UserRequestsDataMapper> getAllRequestForCreator(@Param("userId") String userId,Pageable pageable);

-1

Thay thế / #pagable / bằng? # {# Pagable} cho phép thực hiện phân trang. Thêm PagableDefault cho phép bạn thiết lập kích thước của các phần tử trang.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.