使用 Spring Data MongoDB 存儲庫計數文檔
一、概述
在本教程中,我們將看到使用 Spring Data MongoDB 對集合中的文檔進行計數的不同方法。我們將使用MongoRepository
中可用的所有工具。
我們將使用來自CrudRepository
的註解、查詢方法和方法。此外,我們將構建一個簡單的服務來聚合我們不同的用例。
2.用例設置
我們的用例由模型類、存儲庫和服務類組成。此外,我們將創建一個測試類來幫助我們確保一切都按預期工作。
2.1。創建模型
我們將從創建模型類開始。它將基於汽車的一些屬性:
@Document
public class Car {
private String name;
private String brand;
public Car(String brand) {
this.brand = brand;
}
// getters and setters
}
我們省略了 ID 屬性,因為我們在示例中不需要它。此外,我們正在添加一個構造函數,它以brand
屬性作為參數,以使測試更容易。
2.2.定義存儲庫
讓我們在沒有任何方法的情況下定義我們的存儲庫:
public interface CarRepository extends MongoRepository<Car, String> {
}
我們正在考慮一個字符串 ID,即使我們沒有在我們的模型中聲明一個 ID 屬性。這是因為 MongoDB 創建了一個默認唯一 ID,如果需要,我們仍然可以通過findById()
訪問該 ID。
2.3.定義服務類
我們的服務將以不同的方式利用 Spring Data Repository 接口。
讓我們通過引用我們的存儲庫來定義它:
@Service
public class CountCarService {
@Autowired
private CarRepository repo;
}
我們將在接下來的部分中以此類為基礎,涵蓋示例。
2.4.準備測試
我們所有的測試都將在我們的服務類上運行。我們只需要一些設置,這樣我們就不會得到重複的代碼:
public class CountCarServiceIntegrationTest {
@Autowired
private CountCarService service;
Car car1 = new Car("BA");
@Before
public void init() {
service.insertCar(car1);
service.insertCar(new Car("BB"));
}
}
我們將在每次測試之前運行此塊以簡化我們的測試場景。此外,我們在init()
之外定義car1
以使其在以後的測試中可訪問。
3. 使用CrudRepository
當使用擴展MongoRepository
的CrudRepository
時,我們可以訪問基本功能,包括count()
方法。
3.1。 count()
方法
因此,在我們的第一個計數示例中,我們的存儲庫中沒有任何方法,我們可以在我們的服務中調用它:
public long getCountWithCrudRepository() {
return repo.count();
}
我們可以測試它:
@Test
public void givenAllDocs_whenCrudRepositoryCount_thenCountEqualsSize() {
List<Car> all = service.findCars();
long count = service.getCountWithCrudRepository();
assertEquals(count, all.size());
}
因此,我們確保count()
輸出與我們集合中所有文檔的列表大小相同的數字。
最重要的是,我們必須記住,計數操作比列出所有文檔更具成本效益。這在性能和減少代碼方面都是如此。它不會對小集合產生影響,但對於大集合,我們最終可能會出現 OutOfMemoryError。簡而言之,通過列出整個集合來計算文檔並不是一個好主意。
3.2.使用Example
對象過濾
如果我們想計算具有特定屬性值的文檔, CrudRepository
也可以提供幫助。 count()
方法有一個重載版本,它接收一個 Example 對象:
public long getCountWithExample(Car item) {
return repo.count(Example.of(item));
}
結果,這簡化了任務。現在我們只需用我們想要過濾的屬性填充一個對象,其餘的交給 Spring 完成。讓我們在測試中介紹它:
@Test
public void givenFilteredDocs_whenExampleCount_thenCountEqualsSize() {
long all = service.findCars()
.stream()
.filter(car -> car.getBrand().equals(car1.getBrand()))
.count();
long count = service.getCountWithExample(car1);
assertEquals(count, all);
}
4. 使用@Query 註解
我們的下一個示例將基於@Query
註釋:
@Query(value = "{}", count = true)
Long countWithAnnotation();
我們必須指定value
屬性,否則 Spring 將嘗試從我們的方法名稱創建查詢。但是,由於我們要計算所有文檔,我們只需指定一個空查詢。
然後,我們通過將count
屬性設置為true
來指定此查詢的結果應該是計數投影。
讓我們測試一下:
@Test
public void givenAllDocs_whenQueryAnnotationCount_thenCountEqualsSize() {
List<Car> all = service.findCars();
long count = service.getCountWithQueryAnnotation();
assertEquals(count, all.size());
}
4.1。過濾屬性
我們可以擴展我們的示例,按brand
過濾。讓我們向我們的存儲庫添加一個新方法:
@Query(value = "{brand: ?0}", count = true)
public long countBrand(String brand);
在我們的查詢value
中,我們指定了完整的MongoDB 樣式查詢。 “ ?0
”佔位符代表我們方法的第一個參數,它將是我們的查詢參數值。
MongoDB 查詢有一個 JSON 結構,我們在其中指定字段名稱以及我們想要過濾的值。因此,當我們調用countBrand(“A”)
時,查詢轉換為{brand: “A”}
。這意味著我們將按brand
屬性值為“A”的項目過濾我們的收藏。
5. 編寫派生查詢方法
派生查詢方法是我們存儲庫中不包含帶有value
的@Query
註釋的任何方法。這些方法由 Spring 按名稱解析,因此我們不必編寫查詢。
由於我們的CrudRepository
中已經有一個count()
方法,讓我們創建一個按特定品牌計數的示例:
Long countByBrand(String brand);
此方法將統計所有brand
屬性與參數值匹配的文檔。
現在,讓我們將它添加到我們的服務中:
public long getCountBrandWithQueryMethod(String brand) {
return repo.countByBrand(brand);
}
然後我們通過將其與過濾的流計數操作進行比較來確保我們的方法行為正確:
@Test
public void givenFilteredDocs_whenQueryMethodCountByBrand_thenCountEqualsSize() {
String filter = "BA";
long all = service.findCars()
.stream()
.filter(car -> car.getBrand().equals(filter))
.count();
long count = service.getCountBrandWithQueryMethod(filter);
assertEquals(count, all);
}
當我們只需要編寫幾個不同的查詢時,這很有效。但是,如果我們需要太多不同的計數查詢,可能會變得難以維護。
6. 使用帶條件的動態計數查詢
當我們需要更健壯的東西時,我們可以將Criteria
與Query
對像一起使用。
但是,要運行Query
,我們需要MongoTemplate
。它在啟動期間被實例化,並在SimpleMongoRepository
的mongoOperations
字段中可用。
訪問它的一種方法是擴展SimpleMongoRepository
並創建自定義實現,而不是簡單地擴展MongoRepository
。但是,有一個更簡單的方法。我們可以將它注入到我們的服務中:
@Autowired
private MongoTemplate mongo;
然後我們可以創建新的 count 方法,將Query
傳遞給MongoTemplate
中的count()
方法:
public long getCountBrandWithCriteria(String brand) {
Query query = new Query();
query.addCriteria(Criteria.where("brand")
.is(brand));
return mongo.count(query, Car.class);
}
當我們需要創建動態查詢時,這種方法很有用。我們可以完全控制投影的創建方式。
6.1。使用Example
對象過濾
Criteria
對像還允許我們傳遞一個Example
對象:
public long getCountWithExampleCriteria(Car item) {
Query query = new Query();
query.addCriteria(Criteria.byExample(item));
return mongo.count(query, Car.class);
}
這使得按屬性過濾更容易,同時仍然允許動態部分。
7. 結論
在本文中,我們看到了在 Spring Data MongoDB 中通過存儲庫方法使用計數投影的不同方法。
我們使用了可用的方法,並使用不同的方法創建了新的方法。此外,我們通過將我們的計數方法與列出我們集合中的所有對象進行比較來創建測試。同樣,我們了解了為什麼對這樣的文檔進行計數不是一個好主意。
此外,我們更深入地使用MongoTemplate
創建更多動態計數查詢。
與往常一樣,源代碼可在 GitHub 上獲得。