Spring Data Mongo DB 查詢中的多個條件
一、簡介
在本教程中,我們將探索如何使用 Spring Data JPA 在 MongoDB 中建立具有多個條件的查詢。
2. 設定項目
首先,我們需要在專案中包含必要的依賴項。我們將Spring Data MongoDB 啟動器相依性新增至pom.xml
檔中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>3.3.1</version>
</dependency>
這種依賴關係讓我們在 Spring Boot 專案中使用 Spring Data MongoDB 功能。
2.1.定義 MongoDB 文件和儲存庫
接下來,我們定義一個 MongoDB 文檔,它是一個用@Document
註解的 Java 類別。此類映射到 MongoDB 中的集合。例如,讓我們建立一個Product
文件:
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private String category;
private double price;
private boolean available;
// Getters and setters
}
在Spring Data MongoDB中,我們可以建立一個自訂儲存庫來定義我們自己的查詢方法。透過注入MongoTemplate
,我們可以對 MongoDB 資料庫執行高階操作。此類別提供了一組豐富的方法來執行查詢、聚合資料和有效處理 CRUD 操作:
@ Repository
public class CustomProductRepositoryImpl implements CustomProductRepository {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List find(Query query, Class entityClass) {
return mongoTemplate.find(query, entityClass);
}
}
2.2. MongoDB 中的範例數據
在開始編寫查詢之前,我們假設 MongoDB products
集合中有以下範例資料:
[
{
"name": "MacBook Pro M3",
"price": 1500,
"category": "Laptop",
"available": true
},
{
"name": "MacBook Air M2",
"price": 1000,
"category": "Laptop",
"available": false
},
{
"name": "iPhone 13",
"price": 800,
"category": "Phone",
"available": true
}
]
這些數據將幫助我們有效地測試我們的查詢。
3. 建構 MongoDB 查詢
在 Spring Data MongoDB 中建立複雜查詢時,我們利用andOperator()
和orOperator()
等方法來有效地組合多個條件。這些方法對於建立要求文件同時或交替滿足多個條件的查詢至關重要。
3.1.使用addOperator()
andOperator()
方法用於透過 AND 運算子組合多個條件。這意味著文檔的所有條件都必須滿足才能匹配查詢。當我們需要強制滿足多個條件時,這非常有用。
以下是我們如何使用andOperator()
建構此查詢:
List<Product> findProductsUsingAndOperator(String name, int minPrice, String category, boolean available) {
Query query = new Query();
query.addCriteria(new Criteria().andOperator(Criteria.where("name")
.is(name), Criteria.where("price")
.gt(minPrice), Criteria.where("category")
.is(category), Criteria.where("available")
.is(available)));
return customProductRepository.find(query, Product.class);
}
假設我們要檢索一台名為「 MacBook Pro M3
」、價格超過$1000
的筆記型電腦,並確保它有庫存:
List<Product> actualProducts = productService.findProductsUsingAndOperator("MacBook Pro M3", 1000, "Laptop", true);
assertThat(actualProducts).hasSize(1);
assertThat(actualProducts.get(0).getName()).isEqualTo("MacBook Pro M3");
3.2.使用orOperator()
相反, orOperator()
方法使用 OR 運算子組合多個條件。這意味著文檔必須滿足任一指定條件才能符合查詢。當檢索至少匹配多個條件之一的文件時,這非常有用。
以下是我們如何使用orOperator()
建構此查詢:
List<Product> findProductsUsingOrOperator(String category, int minPrice) {
Query query = new Query();
query.addCriteria(new Criteria().orOperator(Criteria.where("category")
.is(category), Criteria.where("price")
.gt(minPrice)));
return customProductRepository.find(query, Product.class);
}
如果我們想要檢索屬於「 Laptop
」類別或價格大於$1000
產品,我們可以呼叫該方法:
actualProducts = productService.findProductsUsingOrOperator("Laptop", 1000);
assertThat(actualProducts).hasSize(2);
3.3.組合andOperator()
和orOperator()
我們可以透過組合andOperator()
和orOperator()
方法來建立複雜的查詢:
List<Product> findProductsUsingAndOperatorAndOrOperator(String category1, int price1, String name1, boolean available1) {
Query query = new Query();
query.addCriteria(new Criteria().orOperator(
new Criteria().andOperator(
Criteria.where("category").is(category1),
Criteria.where("price").gt(price1)),
new Criteria().andOperator(
Criteria.where("name").is(name1),
Criteria.where("available").is(available1)
)
));
return customProductRepository.find(query, Product.class);
}
在此方法中,我們建立一個Query
物件並使用orOperator()
來定義條件的主要結構。在此,我們使用andOperator()
指定兩個條件。例如,我們可以檢索屬於「 Laptop
」類別、價格超過$1000
或名為「 MacBook Pro M3″
且有庫存的產品:
actualProducts = productService.findProductsUsingAndOperatorAndOrOperator("Laptop", 1000, "MacBook Pro M3", true);
assertThat(actualProducts).hasSize(1);
assertThat(actualProducts.get(0).getName()).isEqualTo("MacBook Pro M3");
3.4.使用鍊式方法
此外,我們可以利用Criteria
類別透過使用and()
方法將多個條件連結在一起,以流暢的方式建構查詢。這種方法提供了一種清晰簡潔的方式來定義複雜的查詢,而不會失去可讀性:
List<Product> findProductsUsingChainMethod(String name1, int price1, String category1, boolean available1) {
Criteria criteria = Criteria.where("name").is(name1)
.and("price").gt(price1)
.and("category").is(category1)
.and("available").is(available1);
return customProductRepository.find(new Query(criteria), Product.class);
}
呼叫此方法時,我們期望找到一款名為「 MacBook Pro M3
」的產品,其價格超過$1000
且有現貨:
actualProducts = productService.findProductsUsingChainMethod("MacBook Pro M3", 1000, "Laptop", true);
assertThat(actualProducts).hasSize(1);
assertThat(actualProducts.get(0).getName()).isEqualTo("MacBook Pro M3");
4. 多條件@Query
註解
除了使用MongoTemplate
的自訂儲存庫之外,我們還可以建立一個新的儲存庫接口,該介面擴展MongoRepository
以利用@Query
註解進行多個條件查詢。這種方法允許我們直接在儲存庫中定義複雜的查詢,而無需以程式設計方式建置它們。
我們可以在ProductRepository
介面中定義一個自訂方法:
public interface ProductRepository extends MongoRepository<Product, String> {
@Query("{ 'name': ?0, 'price': { $gt: ?1 }, 'category': ?2, 'available': ?3 }")
List<Product> findProductsByNamePriceCategoryAndAvailability(String name, double minPrice, String category, boolean available);
@Query("{ $or: [{ 'category': ?0, 'available': ?1 }, { 'price': { $gt: ?2 } } ] }")
List<Product> findProductsByCategoryAndAvailabilityOrPrice(String category, boolean available, double minPrice);
}
第一個方法findProductsByNamePriceCategoryAndAvailability()
檢索與所有指定條件相符的產品。這包括產品的確切名稱、高於指定最低價格的價格、產品所屬的類別以及產品是否有庫存:
actualProducts = productRepository.findProductsByNamePriceCategoryAndAvailability("MacBook Pro M3", 1000, "Laptop", true);
assertThat(actualProducts).hasSize(1);
assertThat(actualProducts.get(0).getName()).isEqualTo("MacBook Pro M3");
另一方面,第二種方法findProductsByCategoryAndAvailabilityOrPrice()
提供了更靈活的方法。它尋找屬於特定類別且可用的產品或價格高於指定最低價格的產品:
actualProducts = productRepository.findProductsByCategoryAndAvailabilityOrPrice("Laptop", false, 600);
assertThat(actualProducts).hasSize(3);
5.使用QueryDSL
QueryDSL 是一個允許我們以程式設計方式建立類型安全性查詢的框架。讓我們逐步了解在 Spring Data MongoDB 專案中設定和使用 QueryDSL 來處理多個條件查詢。
5.1.新增 QueryDSL 依賴項
首先,我們需要將QueryDSL包含在我們的專案中。我們可以透過將以下依賴項新增至pom.xml
檔案來做到這一點:
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>5.1.0</version>
</dependency>
5.2.產生 Q 類
QueryDSL 需要為我們的網域物件產生輔助類別。這些類別通常以「Q」前綴命名(例如QProduct
),提供對實體欄位的類型安全存取。我們可以使用 Maven 插件自動化此生成流程:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
當建置過程執行此配置時,註解處理器會為每個 MongoDB 文件產生 Q 類別。例如,如果我們有一個Product
類,它會產生一個QProduct
類別。這個QProduct
類別提供對Product
實體欄位的類型安全訪問,允許我們使用 QueryDSL 以更結構化且無錯誤的方式建立查詢。
接下來,我們需要修改儲存庫以擴充QuerydslPredicateExecutor
:
public interface ProductRepository extends MongoRepository<Product, String>, QuerydslPredicateExecutor<Product> {
}
5.3.將 AND 與 QueryDSL 結合使用
在 QueryDSL 中,我們可以使用Predicate
介面建構複雜的查詢,該介面表示一個布林運算式。 and()
方法允許我們組合多個條件,確保滿足文件匹配查詢的所有指定條件:
List<Product> findProductsUsingQueryDSLWithAndCondition(String category, boolean available, String name, double minPrice) {
QProduct qProduct = QProduct.product;
Predicate predicate = qProduct.category.eq(category)
.and(qProduct.available.eq(available))
.and(qProduct.name.eq(name))
.and(qProduct.price.gt(minPrice));
return StreamSupport.stream(productRepository.findAll(predicate).spliterator(), false)
.collect(Collectors.toList());
}
在這個方法中,我們首先建立QProduct
的實例。然後,我們使用and()
方法建構一個組合多個條件的Predicate
。最後,我們使用productRepository.findAll(predicate),
它根據建構的謂詞檢索所有符合的產品:
actualProducts = productService.findProductsUsingQueryDSLWithAndCondition("Laptop", true, "MacBook Pro M3", 1000);
assertThat(actualProducts).hasSize(1);
assertThat(actualProducts.get(0).getName()).isEqualTo("MacBook Pro M3");
5.4.將 OR 與 QueryDSL 結合使用
我們也可以使用or()
方法建構查詢,該方法允許我們使用邏輯 OR 運算子組合多個條件。這意味著如果滿足任何指定條件,則文件與查詢相符。
讓我們建立一個使用帶有 OR 條件的 QueryDSL 來尋找產品的方法:
List<Product> findProductsUsingQueryDSLWithOrCondition(String category, String name, double minPrice) {
QProduct qProduct = QProduct.product;
Predicate predicate = qProduct.category.eq(category)
.or(qProduct.name.eq(name))
.or(qProduct.price.gt(minPrice));
return StreamSupport.stream(productRepository.findAll(predicate).spliterator(), false)
.collect(Collectors.toList());
}
or()
方法可確保產品與查詢相符(如果滿足下列任一條件):
actualProducts = productService.findProductsUsingQueryDSLWithOrCondition("Laptop", "MacBook", 800);
assertThat(actualProducts).hasSize(2);
5.4.將 AND 和 OR 與 QueryDSL 結合起來
我們也可以在謂詞中組合and()
和or()
方法。這種靈活性使我們能夠指定一些條件,其中某些條件必須為真,而其他條件可以是替代條件。以下是如何在單一查詢中組合and()
和or()
的範例:
List<Product> findProductsUsingQueryDSLWithAndOrCondition(String category, boolean available, String name, double minPrice) {
QProduct qProduct = QProduct.product;
Predicate predicate = qProduct.category.eq(category)
.and(qProduct.available.eq(available))
.or(qProduct.name.eq(name).and(qProduct.price.gt(minPrice)));
return StreamSupport.stream(productRepository.findAll(predicate).spliterator(), false)
.collect(Collectors.toList());
}
在此方法中,我們透過將條件與and()
和or()
組合來建構查詢。這使我們能夠建立一個查詢,以匹配特定類別中價格大於指定金額的產品或具有可用的特定名稱的產品:
actualProducts = productService.findProductsUsingQueryDSLWithAndOrCondition("Laptop", true, "MacBook Pro M3", 1000);
assertThat(actualProducts).hasSize(3);
六,結論
在本文中,我們探索了在 Spring Data MongoDB 中建立具有多個條件的查詢的各種方法。對於具有幾個條件的簡單查詢, Criteria
或鏈方法可能就足夠了,因為它們很簡單。但是,如果查詢涉及多個條件和巢狀的複雜邏輯,通常建議使用@Query
註解或QueryDSL,因為它們提高了可讀性和可維護性。
與往常一樣,範例的原始程式碼可在 GitHub 上取得。