Mongodb系列- spring-data-mongodb使用MongoTemplate實現分頁查詢

在用spring-data-mongodb框架開發的過程當中,須要實現分頁查詢,就百度了下,沒找到滿意的又google了下,找到了思路.html

在spring-data-mongodb 官方文檔中,建議你使用PagingAndSortingRepository  來實現分頁,可是我是真的不喜歡這個設計啊!!spring

用方法名來映射查詢語句,框架會自動生成執行代碼,還爲此定義了一套語法,舉個例子:mongodb

 
 

public interface UserRepository extends MongoRepository<User, String>, QueryDslPredicateExecutor<User> {
@Query("{ 'name' : ?0 }")
List<User> findUsersByName(String name);框架

 
 

@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
List<User> findUsersByAgeBetween(int ageGT, int ageLT);函數

 
 

List<User> findByName(String name);工具

 
 

List<User> findByNameLikeOrderByAgeAsc(String name);性能

 
 

List<User> findByAgeBetween(int ageGT, int ageLT);測試

 
 

@Query(value = "{}", fields = "{name : 1}")
List<User> findNameAndId();優化

 
 

@Query(value = "{}", fields = "{_id : 0}")
List<User> findNameAndAgeExcludeId();
}this

這個接口類只定義了接口,並不須要實現,由於SDM框架(spring-data-mongodb簡稱,如下都使用簡稱)會幫你生成代碼..

findByAgeBetween(int ageGT, int ageLT);-> 就是where ageGT <age and age <ageLT;

剛開始可能感受很簡單,可是一旦字段多了查詢條件複雜了! 你根本不知道本身在寫什麼!別人看你的代碼一長串方法名,也是直接懵逼的..

而 查出來的許多分頁查詢也是直接使用的PagingAndSortingRepository  這個接口,自動生成...很是不喜歡...就去查怎麼使用MongoTemplate實現...

先下班....放假回來補上..哈哈

慶祝五一上班,把沒寫的寫完...

使用MongoTemplate實現分頁

@Repository("deviceStatusRepository")
public class DeviceStatusRepository {

    @Autowired
    private MongoOperations mongoOperations;


/** * 分頁查詢 */ public PageImpl<DeviceStatusItem> pageDeviceStatusItemByDeviceSerial(String deviceSerial, String collectionName, int pageIndex, int pageSize) { Query query = Query.query( Criteria.where(CONSTS.DEVICE_SERIAL_FIELD).is(deviceSerial)); // 每頁五個 Pageable pageable = new PageRequest(pageIndex, pageSize); // get 5 profiles on a page query.with(pageable); // 排序 query.with(new Sort(Direction.ASC, CONSTS.DEVICE_SERIAL_FIELD, CONSTS.DOMAINID_FIELD)); // 查詢總數 int count = (int) mongoOperations.count(query, DeviceStatusItem.class, collectionName); List<DeviceStatusItem> items = mongoOperations.find(query, DeviceStatusItem.class, collectionName); // System.out.println("stories:" + stories.size() + " count:" + count); return (PageImpl<DeviceStatusItem>) PageableExecutionUtils.getPage(items, pageable, () -> count); }
}

解析:

MongoOperations 是MongoTemplate的接口,它的具體實現就是MongoTemplate,因此這裏使用MongoTemplate或MongoOperations 均可以.

1. 建立PageRequest 對象,這是SDM框架提供的現成的分頁請求類.構造函數很簡單:

// page:第幾頁, size:每頁的大小
public
PageRequest(int page, int size) {
this(page, size, null);
    }

2. 構建Query 查詢條件.我這裏先是指定了根據序列號查詢,而後設置分頁請求 query.with(pageable);最後設置結果的排序.

3. 使用SDM框架自帶的工具類PageableExecutionUtils 返回PageImpl .這裏的PageableExecutionUtils 和PageImpl 其實均可以本身實現,你能夠打開PageImpl 看一下代碼很簡單,就是對查詢結果封裝了下,方便數據的返回.

調用方法:

 1 // 序列號
 2 String deviceSerial="123456";
 3 //集合的名字,就至關於表名
 4 String cllectionName="device";
 5 //返回第幾頁
 6 int pageIndex = 0;
 7 //每頁的大小
 8 int pageSize = 10;
 9 PageImpl<DeviceStatusItem> pageImpl = deviceStatusRepository
10                     .pageDeviceStatusItemByDeviceSerial(deviceSerial, collectionName, pageIndex, pageSize);
11   System.out.println("list:" + pageImpl.getContent() + " number:" + pageImpl.getNumber() + " size:"
12    + pageImpl.getSize() + " pages:" + pageImpl.getTotalPages()
13    + " TotalElements:" + pageImpl.getTotalElements());

解析: 這個PageImpl 方法名很清晰了.

查詢的結果集: pageImpl.getContent(),

當前頁是第幾個: pageImpl.getNumber()

當前頁的大小: pageImpl.getSize()

一共多少頁: pageImpl.getTotalPages()

一共多少條記錄:  pageImpl.getTotalElements()

優化的分頁實現

使用上邊的分頁實現沒有大的問題, 可是有一個性能問題, 當你的集合很大的時候, count每次執行都會全表掃描一下,由於你只有全表掃描才知道有多少數量,耗費不少時間.而這個時間是沒有必要的.

你優化的實現就是去掉count,就想下邊這樣:

    /**
     * deviceSerials分頁查詢,不使用count,否則每次都要全表掃描.
     */
    public PageImpl<DeviceStatusItem> pageDeviceStatusItemByDeviceSerialListNotCount(List<String> deviceSerials,
            String collectionName, int pageIndex, int pageSize) {
        Query query = Query.query(Criteria.where(CONSTS.DEVICE_SERIAL_FIELD).in(deviceSerials));
        // 每頁五個
        Pageable pageable = new PageRequest(pageIndex, pageSize); // get 5 profiles on a page
        query.with(pageable);
        // 排序
        query.with(new Sort(Direction.ASC, CONSTS.DEVICE_SERIAL_FIELD, CONSTS.DOMAINID_FIELD));
        List<DeviceStatusItem> items = readMongoTemplate.find(query, DeviceStatusItem.class, collectionName);
        // System.out.println("stories:" + stories.size() + " count:" + count);
        return (PageImpl<DeviceStatusItem>) PageableExecutionUtils.getPage(items, pageable, () -> 0);
    }

把count去掉就好. 

這樣去掉count後, 只有在最後一次查詢時纔會進行全表掃描.

使用count和不使用count性能比較

1.準備數據:

準備了50萬數據,不是不少,就簡單測試下, 數據量越大效果越明顯.

2.測試程序

只列了主要的程序:

    public static void readUseCount(IDeviceShadowQueryService deviceShadowQueryService, List<String> deviceSerials) {
        int pageIndex = 0;
        int pageSize = 80;
        int totalPages = 0;
        Pagination<DeviceStatusDto> pagination = deviceShadowQueryService
                .readDeviceStatusDtoByDeviceSerials(deviceSerials, pageIndex, pageSize);
        int size = pagination.getRecords().size();
        totalPages = pagination.getTotalPages();
        // 第1頁開始
        for (int i = 1; i < totalPages; i++) {
            pagination = deviceShadowQueryService.readDeviceStatusDtoByDeviceSerials(deviceSerials, i, pageSize);
            totalPages = pagination.getTotalPages();
            size = pagination.getRecords().size();
        }
        count++;
        if (count % 100 == 0)
            System.out.println("totalPages:" + totalPages + " size:" + size);
    }

    public static void readNoCount(IDeviceShadowQueryService deviceShadowQueryService, List<String> deviceSerials) {
        int pageIndex = 0;
        int pageSize = 80;
        Pagination<DeviceStatusDto> page = deviceShadowQueryService
                .readDeviceStatusDtoByDeviceSerialsList(deviceSerials, pageIndex, pageSize);
        int size = page.getRecords().size();
        while (size == pageSize) {
            pageIndex++;
            page = deviceShadowQueryService.readDeviceStatusDtoByDeviceSerialsList(deviceSerials, pageIndex, pageSize);
            size = page.getRecords().size();
        }
        count++;
        if (count % 100 == 0)
            System.out.println("pageIndex:" + pageIndex + " size:" + size);
    }

3.測試結果

使用count,開始讀取, 大小:99975
使用count,讀取完畢,大小:99975 花費時間:112792

不使用count,讀取完畢,大小:99975 花費時間:109696

不使用count,節約時間: 112792-109696=2900= 2.9s

參考: 

 https://stackoverflow.com/questions/27296533/spring-custom-query-with-pageable?rq=1 

 

轉載請註明出處: http://www.cnblogs.com/jycboy/p/8969035.html

相關文章
相關標籤/搜索