使用MongoDB的Spring Boot和MongoTemplate教程

在本教程中,我們將構(gòu)建一個(gè)Spring Boot應(yīng)用程序,該應(yīng)用程序演示如何使用MongoTemplate API訪(fǎng)問(wèn)MongoDB數(shù)據(jù)庫(kù)中的數(shù)據(jù)。

對(duì)于MongoDB臭增,我們將使用mLab,它提供了MongoDB數(shù)據(jù)庫(kù)即服務(wù)平臺(tái)竹习,因此您甚至不必在計(jì)算機(jī)上安裝MongoDB數(shù)據(jù)庫(kù)誊抛。

配置

為了快速設(shè)置我們的項(xiàng)目,我們將使用一個(gè)稱(chēng)為Spring Initializr的工具整陌。使用此工具拗窃,我們可以快速提供所需的依賴(lài)項(xiàng)列表并下載引導(dǎo)程序:

使用Spring Initializr創(chuàng)建新的Spring Boot項(xiàng)目時(shí)瞎领,僅選擇兩個(gè)依賴(lài)項(xiàng):

  • Web (Spring Boot Starter Web)
  • MongoDB (MongoDB Starter)

Maven Dependencies

當(dāng)下載使用Spring Initializr生成的項(xiàng)目并打開(kāi)其pom.xml文件時(shí),您應(yīng)該看到添加了以下依賴(lài)項(xiàng):

<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>

項(xiàng)目結(jié)構(gòu)

在繼續(xù)進(jìn)行并開(kāi)始處理該項(xiàng)目的代碼之前随夸,讓我們介紹一下完成所有代碼添加到項(xiàng)目后將擁有的項(xiàng)目結(jié)構(gòu):

該代碼以多個(gè)程序包進(jìn)行組織九默,因此遵循關(guān)注點(diǎn)分離的原則,并且代碼保持模塊化宾毒。

創(chuàng)建數(shù)據(jù)庫(kù)

對(duì)于MongoDB驼修,我們將使用mLab。您可以使用mLab創(chuàng)建一個(gè)免費(fèi)帳戶(hù)诈铛,并在云中使用MongoDB乙各,而無(wú)需在計(jì)算機(jī)上下載和安裝它。

登錄到mLab后幢竹,您將看到一個(gè)類(lèi)似的儀表板:

要?jiǎng)?chuàng)建一個(gè)新的MongoDB數(shù)據(jù)庫(kù)耳峦,請(qǐng)單擊創(chuàng)建新按鈕:

輸入所有詳細(xì)信息后,我們可以確認(rèn):

完成此操作后妨退,我們將看到一個(gè)連接字符串妇萄,如下圖所示:

在開(kāi)始使用此數(shù)據(jù)庫(kù)之前,我們還需要?jiǎng)?chuàng)建一個(gè)用戶(hù)咬荷。讓我們?cè)谏蠄D所示的“用戶(hù)”標(biāo)簽上執(zhí)行此操作:

現(xiàn)在,我們已經(jīng)準(zhǔn)備好繼續(xù)寫(xiě)些Java代碼轻掩,因?yàn)槲覀兊膍Lab數(shù)據(jù)庫(kù)已經(jīng)完全準(zhǔn)備就緒幸乒。

應(yīng)用配置

使用Spring Boot,僅使用MongoDB連接String的單個(gè)必需屬性即可輕松配置我們的應(yīng)用程序:

# application propertiesserver.port=8090# MongoDB propertiesspring.data.mongodb.uri=mongodb://cicoding.cn:password@ds915721.mlab.com:29670/cicoding_db

我們只是提供了一個(gè)MongoDB連接字符串唇牧,該字符串將由Spring Boot讀取罕扎,并且將使用內(nèi)部API建立連接。

建立模型

我們將創(chuàng)建一個(gè)簡(jiǎn)單的Person實(shí)體丐重,其中包含一些字段腔召,我們將使用它們來(lái)演示簡(jiǎn)單的MongoDB查詢(xún):

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);   }}

除了簡(jiǎn)單的字段外,我們還添加了一些幫助程序功能扮惦,可以在保存用戶(hù)的出生日期時(shí)計(jì)算該用戶(hù)的年齡臀蛛。這使我們不必計(jì)算用戶(hù)的年齡。

定義數(shù)據(jù)訪(fǎng)問(wèn)層接口

讓我們定義一個(gè)數(shù)據(jù)層接口崖蜜,該接口將通知我們?cè)趹?yīng)用程序中將演示多少操作浊仆。這是界面:

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);}

這些是相當(dāng)多的操作。真正的樂(lè)趣是當(dāng)我們執(zhí)行這些操作時(shí)豫领,接下來(lái)將要做的事情抡柿。

實(shí)施數(shù)據(jù)訪(fǎng)問(wèn)層

我們將使用MongoTemplate bean,它是由Spring Boot使用上面在application.properties中定義的屬性初始化的等恐。讓我們看看如何定義所需的bean:

@Repositorypublic class PersonDALImpl implements PersonDAL {    private final MongoTemplate mongoTemplate;    @Autowired    public PersonDALImpl(MongoTemplate mongoTemplate) {        this.mongoTemplate = mongoTemplate;    }    ...}

我們將開(kāi)始使用簡(jiǎn)單的方法來(lái)理解查詢(xún)洲劣,首先是要保存并從數(shù)據(jù)庫(kù)中獲取所有人員:

@Overridepublic Person savePerson(Person person) {   mongoTemplate.save(person);   return person;}@Overridepublic List<Person> getAllPerson() {   return mongoTemplate.findAll(Person.class);}

MongoTemplate為我們提供了一些抽象方法备蚓,通過(guò)這些方法我們可以將對(duì)象保存到數(shù)據(jù)庫(kù)中,也可以從數(shù)據(jù)庫(kù)中獲取所有數(shù)據(jù)囱稽。

使用分頁(yè)查詢(xún)

上述從數(shù)據(jù)庫(kù)中獲取所有人的方法的問(wèn)題在于星著,數(shù)據(jù)庫(kù)中可能有數(shù)千個(gè)對(duì)象。 我們應(yīng)該始終在查詢(xún)中實(shí)現(xiàn)分頁(yè)粗悯,以便可以確保僅從數(shù)據(jù)庫(kù)中提取有限的數(shù)據(jù):

@Overridepublic List<Person> getAllPersonPaginated(int pageNumber, int pageSize) {   Query query = new Query();   query.skip(pageNumber * pageSize);   query.limit(pageSize);   return mongoTemplate.find(query, Person.class);}

這樣虚循,一次將僅從數(shù)據(jù)庫(kù)中獲取pageSize個(gè)對(duì)象。

通過(guò)精確值獲取對(duì)象

我們也可以通過(guò)匹配數(shù)據(jù)庫(kù)中的精確值來(lái)提取對(duì)象:

@Overridepublic Person findOneByName(String name) {   Query query = new Query();   query.addCriteria(Criteria.where("name").is(name));   return mongoTemplate.findOne(query, Person.class);}@Overridepublic List<Person> findByName(String name) {   Query query = new Query();   query.addCriteria(Criteria.where("name").is(name));   return mongoTemplate.find(query, Person.class);}

我們展示了兩種方法样傍。第一種方法是從數(shù)據(jù)庫(kù)中獲取單個(gè)對(duì)象横缔,而第二種方法是從數(shù)據(jù)庫(kù)中獲取具有匹配條件的所有對(duì)象。

按范圍和數(shù)據(jù)列表查找

我們還可以找到具有指定范圍內(nèi)的字段值的對(duì)象衫哥【ジ眨或具有特定日期之后的日期數(shù)據(jù)的對(duì)象。讓我們看看如何做到這一點(diǎn)撤逢,以及如何構(gòu)造相同的查詢(xún):

@Overridepublic List<Person> findByBirthDateAfter(Date date) {   Query query = new Query();   query.addCriteria(Criteria.where("dateOfBirth").gt(date));   return mongoTemplate.find(query, Person.class);}@Overridepublic 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);}@Overridepublic List<Person> findByFavoriteBooks(String favoriteBook) {   Query query = new Query();   query.addCriteria(Criteria.where("favoriteBooks").in(favoriteBook));   return mongoTemplate.find(query, Person.class);}

更新對(duì)象

我們可以使用Update查詢(xún)更新MongoDB中的數(shù)據(jù)膛锭。我們可以找到一個(gè)對(duì)象,然后自己更新提供的字段:

@Overridepublic void updateMultiplePersonAge() {   Query query = new Query();   Update update = new Update().inc("age", 1);   mongoTemplate.findAndModify(query, update, Person.class);;}@Overridepublic Person updateOnePerson(Person person) {   mongoTemplate.save(person);   return person;}

在第一個(gè)查詢(xún)中蚊荣,由于沒(méi)有向查詢(xún)添加任何條件初狰,因此我們收到了所有對(duì)象。接下來(lái)互例,我們提供了一個(gè)Update子句奢入,其中所有用戶(hù)的年齡都增加了一個(gè)。

刪除對(duì)象

刪除對(duì)象也與單個(gè)方法調(diào)用有關(guān):

@Overridepublic void deletePerson(Person person) {   mongoTemplate.remove(person);}

我們也可以簡(jiǎn)單地傳遞Query對(duì)象以及要?jiǎng)h除的人的ID媳叨。

制作命令行運(yùn)行器

我們將在適當(dāng)?shù)奈恢檬褂妹钚羞\(yùn)行程序來(lái)運(yùn)行我們的應(yīng)用程序腥光,該命令行運(yùn)行程序?qū)⑻峁┪覀冊(cè)谏厦娴臄?shù)據(jù)訪(fǎng)問(wèn)層實(shí)現(xiàn)中定義的一些功能。這是命令行運(yùn)行器:

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;@SpringBootApplicationpublic 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));  }}

我們可以使用一個(gè)簡(jiǎn)單的命令來(lái)運(yùn)行我們的應(yīng)用程序:

mvn spring-boot:run

運(yùn)行應(yīng)用程序后糊秆,我們將能夠在終端中看到一個(gè)簡(jiǎn)單的輸出:

結(jié)論

如果我們將MongoTemplate與簡(jiǎn)單的Spring Data JPA進(jìn)行比較武福,它看起來(lái)可能很復(fù)雜,但是它也使我們對(duì)如何構(gòu)造查詢(xún)有更多的控制痘番。

Spring Data JPA過(guò)多地提取了有關(guān)構(gòu)造什么查詢(xún)捉片,將哪些條件傳遞給查詢(xún)等的詳細(xì)信息。 使用MongoTemplate夫偶,我們可以對(duì)查詢(xún)組成進(jìn)行更細(xì)粒度的控制界睁,Spring Boot為我們提供了易于開(kāi)發(fā)的應(yīng)用程序。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兵拢,一起剝皮案震驚了整個(gè)濱河市翻斟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌说铃,老刑警劉巖访惜,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘹履,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡债热,警方通過(guò)查閱死者的電腦和手機(jī)砾嫉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窒篱,“玉大人焕刮,你說(shuō)我怎么就攤上這事∏奖” “怎么了配并?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)高镐。 經(jīng)常有香客問(wèn)我溉旋,道長(zhǎng),這世上最難降的妖魔是什么嫉髓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任观腊,我火速辦了婚禮,結(jié)果婚禮上算行,老公的妹妹穿的比我還像新娘梧油。我一直安慰自己,他們只是感情好纱意,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布婶溯。 她就那樣靜靜地躺著,像睡著了一般偷霉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上褐筛,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天类少,我揣著相機(jī)與錄音,去河邊找鬼渔扎。 笑死硫狞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晃痴。 我是一名探鬼主播残吩,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼倘核!你這毒婦竟也來(lái)了泣侮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤紧唱,失蹤者是張志新(化名)和其女友劉穎活尊,沒(méi)想到半個(gè)月后隶校,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛹锰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年深胳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铜犬。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舞终,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癣猾,到底是詐尸還是另有隱情敛劝,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布煎谍,位于F島的核電站攘蔽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏呐粘。R本人自食惡果不足惜满俗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望作岖。 院中可真熱鬧唆垃,春花似錦、人聲如沸痘儡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沉删。三九已至渐尿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矾瑰,已是汗流浹背砖茸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殴穴,地道東北人凉夯。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像采幌,于是被迫代替她去往敵國(guó)和親劲够。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容