在本教程中,我們將構建一個Spring Boot應用程序,該應用程序演示如何使用MongoTemplate API訪問MongoDB數據庫中的數據。
對於MongoDB,我們將使用mLab,它提供了MongoDB數據庫即服務平台,因此您甚至不必在計算機上安裝MongoDB數據庫。
配置
為了快速設置我們的項目,我們將使用一個稱為Spring Initializr的工具。使用此工具,我們可以快速提供所需的依賴項列表並下載引導程序:

使用Spring Initializr創建新的Spring Boot項目時,僅選擇兩個依賴項:
- Web (Spring Boot Starter Web)
- MongoDB (MongoDB Starter)
Maven Dependencies
當下載使用Spring Initializr生成的項目並打開其pom.xml文件時,您應該看到添加了以下依賴項:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
項目結構
在繼續進行並開始處理該項目的代碼之前,讓我們介紹一下完成所有代碼添加到項目后將擁有的項目結構:

該代碼以多個程序包進行組織,因此遵循關注點分離的原則,並且代碼保持模塊化。
創建數據庫
對於MongoDB,我們將使用mLab。您可以使用mLab創建一個免費帳戶,並在雲中使用MongoDB,而無需在計算機上下載和安裝它。
登錄到mLab后,您將看到一個類似的儀表板:

要創建一個新的MongoDB數據庫,請單擊創建新按鈕:

輸入所有詳細信息后,我們可以確認:

完成此操作后,我們將看到一個連接字符串,如下圖所示:

在開始使用此數據庫之前,我們還需要創建一個用戶。讓我們在上圖所示的“用戶”標簽上執行此操作:

現在,我們已經准備好繼續寫些Java代碼,因為我們的mLab數據庫已經完全准備就緒。
應用配置
使用Spring Boot,僅使用MongoDB連接String的單個必需屬性即可輕松配置我們的應用程序:
# application properties
server.port=8090
# MongoDB properties
spring.data.mongodb.uri=mongodb://cicoding.cn:password@ds915721.mlab.com:29670/cicoding_db
我們只是提供了一個MongoDB連接字符串,該字符串將由Spring Boot讀取,並且將使用內部API建立連接。
建立模型
我們將創建一個簡單的Person實體,其中包含一些字段,我們將使用它們來演示簡單的MongoDB查詢:
package com.cicoding.mongotemplatedemo.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import static java.util.Calendar.DATE;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.YEAR;
@Document(collection = "person")
public class Person {
@Id
private String personId;
private String name;
private long age;
private List<String> favoriteBooks;
private Date dateOfBirth;
public Person() {
}
public Person(String name, List<String> childrenName, Date dateOfBirth) {
this.name = name;
this.favoriteBooks = childrenName;
this.dateOfBirth = dateOfBirth;
this.age = getDiffYears(dateOfBirth, new Date());
}
// standard getters and setters
private int getDiffYears(Date first, Date last) {
Calendar a = getCalendar(first);
Calendar b = getCalendar(last);
int diff = b.get(YEAR) - a.get(YEAR);
if (a.get(MONTH) > b.get(MONTH) ||
(a.get(MONTH) == b.get(MONTH) && a.get(DATE) > b.get(DATE))) {
diff--;
}
return diff;
}
private Calendar getCalendar(Date date) {
Calendar cal = Calendar.getInstance(Locale.US);
cal.setTime(date);
return cal;
}
@Override
public String toString() {
return String.format("Person{personId='%s', name='%s', age=%d, dateOfBirth=%s}\n",
personId, name, age, dateOfBirth);
}
}
除了簡單的字段外,我們還添加了一些幫助程序功能,可以在保存用戶的出生日期時計算該用戶的年齡。這使我們不必計算用戶的年齡。
定義數據訪問層接口
讓我們定義一個數據層接口,該接口將通知我們在應用程序中將演示多少操作。這是界面:
public interface PersonDAL {
Person savePerson(Person person);
List<Person> getAllPerson();
List<Person> getAllPersonPaginated(
int pageNumber, int pageSize);
Person findOneByName(String name);
List<Person> findByName(String name);
List<Person> findByBirthDateAfter(Date date);
List<Person> findByAgeRange(int lowerBound, int upperBound);
List<Person> findByFavoriteBooks(String favoriteBook);
void updateMultiplePersonAge();
Person updateOnePerson(Person person);
void deletePerson(Person person);
}
這些是相當多的操作。真正的樂趣是當我們執行這些操作時,接下來將要做的事情。
實施數據訪問層
我們將使用MongoTemplate bean,它是由Spring Boot使用上面在application.properties中定義的屬性初始化的。讓我們看看如何定義所需的bean:
@Repository
public class PersonDALImpl implements PersonDAL {
private final MongoTemplate mongoTemplate;
@Autowired
public PersonDALImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
...
}
我們將開始使用簡單的方法來理解查詢,首先是要保存並從數據庫中獲取所有人員:
@Override
public Person savePerson(Person person) {
mongoTemplate.save(person);
return person;
}
@Override
public List<Person> getAllPerson() {
return mongoTemplate.findAll(Person.class);
}
MongoTemplate為我們提供了一些抽象方法,通過這些方法我們可以將對象保存到數據庫中,也可以從數據庫中獲取所有數據。
使用分頁查詢
上述從數據庫中獲取所有人的方法的問題在於,數據庫中可能有數千個對象。 我們應該始終在查詢中實現分頁,以便可以確保僅從數據庫中提取有限的數據:
@Override
public List<Person> getAllPersonPaginated(int pageNumber, int pageSize) {
Query query = new Query();
query.skip(pageNumber * pageSize);
query.limit(pageSize);
return mongoTemplate.find(query, Person.class);
}
這樣,一次將僅從數據庫中獲取pageSize個對象。
通過精確值獲取對象
我們也可以通過匹配數據庫中的精確值來提取對象:
@Override
public Person findOneByName(String name) {
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name));
return mongoTemplate.findOne(query, Person.class);
}
@Override
public List<Person> findByName(String name) {
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name));
return mongoTemplate.find(query, Person.class);
}
我們展示了兩種方法。第一種方法是從數據庫中獲取單個對象,而第二種方法是從數據庫中獲取具有匹配條件的所有對象。
按范圍和數據列表查找
我們還可以找到具有指定范圍內的字段值的對象。或具有特定日期之后的日期數據的對象。讓我們看看如何做到這一點,以及如何構造相同的查詢:
@Override
public List<Person> findByBirthDateAfter(Date date) {
Query query = new Query();
query.addCriteria(Criteria.where("dateOfBirth").gt(date));
return mongoTemplate.find(query, Person.class);
}
@Override
public List<Person> findByAgeRange(int lowerBound, int upperBound) {
Query query = new Query();
query.addCriteria(Criteria.where("age").gt(lowerBound)
.andOperator(Criteria.where("age").lt(upperBound)));
return mongoTemplate.find(query, Person.class);
}
@Override
public List<Person> findByFavoriteBooks(String favoriteBook) {
Query query = new Query();
query.addCriteria(Criteria.where("favoriteBooks").in(favoriteBook));
return mongoTemplate.find(query, Person.class);
}
更新對象
我們可以使用Update查詢更新MongoDB中的數據。我們可以找到一個對象,然后自己更新提供的字段:
@Override
public void updateMultiplePersonAge() {
Query query = new Query();
Update update = new Update().inc("age", 1);
mongoTemplate.findAndModify(query, update, Person.class);;
}
@Override
public Person updateOnePerson(Person person) {
mongoTemplate.save(person);
return person;
}
在第一個查詢中,由於沒有向查詢添加任何條件,因此我們收到了所有對象。接下來,我們提供了一個Update子句,其中所有用戶的年齡都增加了一個。
刪除對象
刪除對象也與單個方法調用有關:
@Override
public void deletePerson(Person person) {
mongoTemplate.remove(person);
}
我們也可以簡單地傳遞Query對象以及要刪除的人的ID。
制作命令行運行器
我們將在適當的位置使用命令行運行程序來運行我們的應用程序,該命令行運行程序將提供我們在上面的數據訪問層實現中定義的一些功能。這是命令行運行器:
package com.cicoding.mongotemplatedemo;
import com.cicoding.mongotemplatedemo.dal.PersonDAL;
import com.cicoding.mongotemplatedemo.model.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Arrays;
import java.util.Date;
@SpringBootApplication
public class MongoTemplateApp implements CommandLineRunner {
private static final Logger LOG = LoggerFactory.getLogger("cicoding");
private final PersonDAL personDAL;
@Autowired
public MongoTemplateApp(PersonDAL personDAL) {
this.personDAL = personDAL;
}
public static void main(String[] args) {
SpringApplication.run(MongoTemplateApp.class, args);
}
@Override
public void run(String... args) {
personDAL.savePerson(new Person(
"Shubham", Arrays.asList("Harry potter", "Waking Up"), new Date(769372200000L)));
personDAL.savePerson(new Person(
"Sergey", Arrays.asList("Startup Guides", "Java"), new Date(664309800000L)));
personDAL.savePerson(new Person(
"David", Arrays.asList("Harry potter", "Success"), new Date(695845800000L)));
personDAL.savePerson(new Person(
"Ivan", Arrays.asList("Secrets of Butene", "Meeting Success"), new Date(569615400000L)));
personDAL.savePerson(new Person(
"Sergey", Arrays.asList("Harry potter", "Startup Guides"), new Date(348777000000L)));
LOG.info("Getting all data from MongoDB: \n{}",
personDAL.getAllPerson());
LOG.info("Getting paginated data from MongoDB: \n{}",
personDAL.getAllPersonPaginated(0, 2));
LOG.info("Getting person By name 'Sergey': {}",
personDAL.findByName("Sergey"));
LOG.info("Getting all person By name 'Sergey': {}",
personDAL.findOneByName("Sergey"));
LOG.info("Getting people between age 22 & 26: {}",
personDAL.findByAgeRange(22, 26));
}
}
我們可以使用一個簡單的命令來運行我們的應用程序:
mvn spring-boot:run
運行應用程序后,我們將能夠在終端中看到一個簡單的輸出:

結論
如果我們將MongoTemplate與簡單的Spring Data JPA進行比較,它看起來可能很復雜,但是它也使我們對如何構造查詢有更多的控制。
Spring Data JPA過多地提取了有關構造什么查詢,將哪些條件傳遞給查詢等的詳細信息。 使用MongoTemplate,我們可以對查詢組成進行更細粒度的控制,Spring Boot為我們提供了易於開發的應用程序。
