簡體   English   中英

可分頁排序在 Spring 數據 JPA、Spring 框架中不起作用

[英]Pageable Sorting not working in Spring Data JPA, Spring Framework

我正在嘗試為一個人實現跨許多屬性的搜索功能。

這是 model。

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Person {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "USER_ID")
    private long id;
    
    private String firstName;
    
    private String surName;
    
    private int age;
    
    private Date DOB;
    
    private String description;
    
    private String highestEducationQualification;
    
    private String occupation;
    
    private String employer;
    
    private String college;
    
    private String school;
    
    private String eyecolor;
    
    private double weight;
    
    private double height;
    
    private String PPSnumber;
    
    private boolean driversLicence;
    
    private boolean provisionalLicence;
    
    private String bankIBAN;
    
    private long phoneNumber;
    
    private char gender;
    
    private String emailAddress;
    
    private String websiteAddress;
    
    private String homeAddress;
    
}

這是我的存儲庫。

@Repository
public interface PersonRepo extends JpaRepository<Person, Long>{
    
    List<Person> searchByFirstNameContainingAllIgnoreCase(String firstName, Pageable page);
    List<Person> searchBySurNameContainingAllIgnoreCase(String surName, Pageable page);
    List<Person> searchByAge(int age, Pageable page);
    List<Person> searchByDescriptionContainingAllIgnoreCase(String desc, Pageable page);
    List<Person> searchByHighestEducationQualificationContainingAllIgnoreCase(String edu, Pageable page);
    List<Person> searchByOccupationContainingAllIgnoreCase(String occ, Pageable page);
    List<Person> searchByEmployerContainingAllIgnoreCase(String emp, Pageable page);
    List<Person> searchByCollegeContainingAllIgnoreCase(String emp, Pageable page);
    List<Person> searchBySchoolContainingAllIgnoreCase(String emp, Pageable page);
    List<Person> searchByEyecolorContainingAllIgnoreCase(String eye, Pageable page);
    List<Person> searchByWeight(double weight, Pageable page);
    List<Person> searchByHeight(double height, Pageable page);
    List<Person> searchByPPSnumberIgnoreCase(String emp, Pageable page);
    List<Person> searchByDriversLicence(boolean emp, Pageable page);
    List<Person> searchByProvisionalLicence(boolean emp, Pageable page);
    List<Person> searchByBankIBANAllIgnoreCase(String emp, Pageable page);
    List<Person> searchByPhoneNumber(long phone, Pageable page);
    List<Person> searchByGender(char emp, Pageable page);
    List<Person> searchByEmailAddressIgnoreCase(String emp, Pageable page);
    List<Person> searchByWebsiteAddressContainingAllIgnoreCase(String emp, Pageable page);
    List<Person> searchByHomeAddressContainingAllIgnoreCase(String emp, Pageable page);
}

服務 function。

class PersonService {

    @Autowired
    private PersonRepo personRepo;

    @Override
    public List<Person> searchByAllAttributes(String toSearch, int page, int quan, String sortBy, boolean ascending) {
        int ageToSearch = 0;
        try {
            ageToSearch = Integer.parseInt(toSearch);
        } catch (Exception e) {
            
        }
        double toSearchDouble = 0;
        try {
            toSearchDouble = Double.parseDouble(toSearch);
        } catch (Exception e) {
            
        }
        long phoneToSearch = 0;
        try {
            phoneToSearch = Long.parseLong(toSearch);
        } catch (Exception e) {
            
        }
        
        System.out.println(toSearchDouble);
        
        List<Person> results;
        
        Pageable firstPageWithTwoElements = PageRequest.of(page, quan, Sort.by("firstName").descending());
        
        results = personRepo.searchByFirstNameContainingAllIgnoreCase(toSearch, firstPageWithTwoElements);
        results.addAll(personRepo.searchBySurNameContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByAge(ageToSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByDescriptionContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        
        results.addAll(personRepo.searchByCollegeContainingAllIgnoreCase(toSearch, firstPageWithTwoElements));
        results.addAll(personRepo.searchBySchoolContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByEmployerContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByOccupationContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByHighestEducationQualificationContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByEyecolorContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByWeight(toSearchDouble,firstPageWithTwoElements));
        results.addAll(personRepo.searchByHeight(toSearchDouble,firstPageWithTwoElements));
        results.addAll(personRepo.searchByPPSnumberIgnoreCase(toSearch,firstPageWithTwoElements));
        //drivers and provisional
        results.addAll(personRepo.searchByBankIBANAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByPhoneNumber(phoneToSearch,firstPageWithTwoElements));
        //gender
        results.addAll(personRepo.searchByEmailAddressIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByWebsiteAddressContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        results.addAll(personRepo.searchByHomeAddressContainingAllIgnoreCase(toSearch,firstPageWithTwoElements));
        
        results = removeDuplicatePersons(results);
        
        return results;
    }

    List<Person> removeDuplicatePersons(List<Person> toRemove){
        List<Person> result = toRemove;
        
         List<Person> listWithoutDuplicates = new ArrayList<>(
                  new HashSet<Person>(result));
         
        return listWithoutDuplicates;
    }

}

正如您將看到的,有一個硬編碼的排序 object,帶有名字和降序。 每當我調用這個 function 時,它都會返回一個隨機排序的數據。 排序不起作用。 我努力對其進行硬編碼以消除參數數據損壞的可能性,但即使是硬編碼也不起作用。

toSearch是一個字符串搜索查詢。 Pagequan (數量)用於分頁。 分頁有效,但排序無效。 任何幫助表示贊賞,如果您需要我更多地解釋代碼,請添加評論。

正如您可能想象的那樣,還有一個 controller class 。 我也可以添加該代碼,但它不會直接影響該代碼的邏輯。 controller 調用服務 function 並將其作為 JSON 返回給 Z2567A5EC3705EB7AC2C1E96 應用程序。 I have debugged both in Postman, by requesting the REST controller function which invokes the service function. 它以 JSON 的形式將數據返回給 Postman,我在實現 web 應用程序時也做了同樣的事情,但數據沒有排序。

您會注意到 Person class model 上的 4 個注釋。 實體是為了持久化。 NoArgsConstructor 和 Getter 和 Setter 是 Lombok 的一部分,package 允許您省略 getter、setter、構造函數,它們是在編譯時添加的。

問題不在於排序。 問題在於您執行搜索的方式。 您調用 18 個不同的搜索並將它們合並。 每個搜索都已排序,但不能保證您的results列表已排序。

例子:

  1. 第一次搜索返回: {"Betty", "Adam"} (按名字降序正確排序)
  2. 第二次搜索返回: {"Zack", "Fiona"} (按名字降序正確排序)

但結果: {"Betty", "Adam", "Zack", "Fiona"}看起來像一個隨機順序。

您始終可以在 Java 端進行排序,但由於大型列表的性能問題,不建議這樣做。

要解決此問題,您需要使用一個查詢進行搜索。 您可以按規范使用 spring 引導查詢來實現這一點,您可以在此處找到更多信息。

您可能正在尋找 JPA 標准 API。 您可以使用 JPA 規范 API 構建您的查詢,它比您正在做的事情有兩個明顯的優勢:

  1. 可以在一個查詢中進行查詢(更高性能)。
  2. 構建后更容易更改和適應(如果您設計好類/模式)。

在這里給你一個簡單的例子:

@Repository
public interface PersonRepo extends JpaRepository<Person,Long>, JpaSpecificationExecutor<Person> {}

這是我寫的一個快速組件。 您可以看到它在哪里使用 JPA 規范 API 創建 SQL,然后使用 Repo 運行它。

@Component
public class PersonSearcher {

    @Autowired
    private PersonRepo personRepo;

    /*
        Would be better taking a "Form"/Object with your search criteria.
     */
    public Page<Person> search(String name, Integer ageMin, Integer ageMax, Pageable pageable) {

        //Get "all"
        Specification<Person> personSpecification = Specification.not(null);

        //Create "Predicates" (like the where clauses).
        if (name != null) {
            personSpecification = personSpecification.and(new MyPersonSpec("firstName", name, "like"));
        }
        if (ageMin != null) {
            personSpecification = personSpecification.and(new MyPersonSpec("age", ageMin, "gt"));
        }

        if (ageMax != null) {
            personSpecification = personSpecification.and(new MyPersonSpec("age", ageMax, "lt"));
        }


        //Run query using Repo. Spring paging still works.
        return personRepo.findAll(personSpecification, pageable);

    }

    private static class MyPersonSpec implements Specification<Person> {

        private final String field;
        private final Object value;
        private final String operation;

        private MyPersonSpec(String field, Object value, String operation) {
            this.field = field;
            this.value = value;
            this.operation = operation;
        }

        @Override
        public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            switch (operation) {
                case "like":
                    return criteriaBuilder.like(root.get(field), "%" + value.toString().toLowerCase() + "%");
                case "equal":
                    return criteriaBuilder.equal(root.get(field), value);
                case "gt":
                    return criteriaBuilder.greaterThan(root.get(field), (int) value);
                case "lt":
                    return criteriaBuilder.lessThan(root.get(field), (int) value);

                default:
                    throw new RuntimeException("Unexpected `op`.");
            }
        }
    }
}

這是一個快速測試以確保它可以編譯...您需要做一些正確的斷言,也許是@DataJpa 測試而不是@SpringBootTest...

@DataJpaTest
@ActiveProfiles("tc")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Log4j2
@Transactional
@Sql(statements = {
        "INSERT INTO person (USER_ID,first_name,age,drivers_licence,provisional_licence) values(1,'Bob',31,true,false)",
        "INSERT INTO person (USER_ID,first_name,age,drivers_licence,provisional_licence) values(2,'Alice',56,true,false)",
        "INSERT INTO person (USER_ID,first_name,age,drivers_licence,provisional_licence) values(3,'Charlie',18,false,false)",
        "INSERT INTO person (USER_ID,first_name,age,drivers_licence,provisional_licence) values(4,'Dave',72,true,true)",
        "INSERT INTO person (USER_ID,first_name,age,drivers_licence,provisional_licence) values(5,'Emma',21,false,true)"
})
@Import(PersonSearcher.class)
class PersonSearcherTest {

    @Autowired
    private PersonSearcher personSearcher;

    @Test
    void search() {

        /*
         * Test 1 : Finds Bob using name.
         */

        //Run the searcher to find "Bob".
        final Page<Person> search = personSearcher.search("bob", null, null,
                PageRequest.of(0, 5, Sort.by(Sort.Direction.DESC, "emailAddress"))
        );
        log.info(search);

        //Assert Returns Bob (ID 1)
        assertEquals(1L, search.getContent().get(0).getId());

        /*
         * Test 2: Name and age range with an order by age in Paging.
         */

        //Search with any name with an A/a in it between ages 20->99.
        final Page<Person> search2 = personSearcher.search("a", 20, 100,
                PageRequest.of(0, 5, Sort.Direction.ASC, "age"));

        log.info(search2.getContent());
        //Assert fetches only ones with an `a` or `A` and should have age >20 and <100.
        assertTrue(search2
                .stream()
                .allMatch(it ->
                        it.getFirstName().toLowerCase().contains("a")
                                && it.getAge() > 20
                                && it.getAge() < 100
                ));

        //Assert they are Alice,Dave and Emma (NOT Charlie as <20 age) and in age order from youngest to oldest...
        Assertions.assertEquals(Arrays.asList("Emma", "Alice", "Dave"),
                search2.get().map(Person::getFirstName).collect(Collectors.toList()));


        /*
         * Test 3 : With null values gets all back with order by firstName ASC
         */

        final Page<Person> allSearch = personSearcher.search(null, null, null,
                PageRequest.of(0, 10, Sort.Direction.ASC, "firstName"));

        //Assert all back in name order.
        assertEquals(Arrays.asList("Alice", "Bob", "Charlie", "Dave", "Emma"),
                allSearch.get().map(Person::getFirstName).collect(Collectors.toList()));
    }
}

SQL 規范產生並在日志中運行:

08 Jan 2021 23:24:19,840 [DEBUG] --- o.h.SQL                        : 
    select
        person0_.user_id as user_id1_18_,
        person0_.dob as dob2_18_,
        person0_.ppsnumber as ppsnumbe3_18_,
        person0_.age as age4_18_,
        person0_.bankiban as bankiban5_18_,
        person0_.college as college6_18_,
        person0_.description as descript7_18_,
        person0_.drivers_licence as drivers_8_18_,
        person0_.email_address as email_ad9_18_,
        person0_.employer as employe10_18_,
        person0_.eyecolor as eyecolo11_18_,
        person0_.first_name as first_n12_18_,
        person0_.gender as gender13_18_,
        person0_.height as height14_18_,
        person0_.highest_education_qualification as highest15_18_,
        person0_.home_address as home_ad16_18_,
        person0_.occupation as occupat17_18_,
        person0_.phone_number as phone_n18_18_,
        person0_.provisional_licence as provisi19_18_,
        person0_.school as school20_18_,
        person0_.sur_name as sur_nam21_18_,
        person0_.website_address as website22_18_,
        person0_.weight as weight23_18_ 
    from
        person person0_ 
    where
        person0_.age<20 
        and person0_.age>1 
        and (
            person0_.first_name like ?
        ) 
    order by
        person0_.email_address desc limit ?

暫無
暫無

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

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