MongoDB是一款開源的文檔型數據庫。
NoSQL可以分為四大塊:
- K-V類型:redis、MemberCached
- 文檔型:MongoDB、Couchbase
- 列存儲:Cassandra、HBase
- 圖存儲:Neo4j
啟動MongoDB服務
通過Docker引擎啟動MongoDB服務。這里有MongoDB容器的相關說明。
獲取鏡像
docker pull mongo
執行如上命令獲取最新的mongo鏡像。
運行MongoDB鏡像
docker run --name mongo -p 27017:27017 -v /Users/lucky/Documents/workspace/docker_mapping_volume/mongo:/data/db -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin -d mongo
- --name mongo:指定容器名稱為mongo
- -p 27017:27017:指定宿主機和容器的映射端口,[宿主機端口]:[容器端口]
- -v [宿主機目錄]:[容器目錄]
- -e 環境變量設置,設置賬號、密碼
- -d 后台執行
查看運行的鏡像
docker ps
登陸到MongoDB容器中
docker exec -it mongo bash
通過shell連接MongoDB
mongo -u admin -p admin
初始化MongoDB的庫及權限
為項目創建對應的數據庫,以及有讀寫該庫權限的用戶。
創建庫
use springbucks;
使用use
命令用來選擇數據庫,當我們在該庫上做操作時,MongoDB就會自動為我們創建數據庫。
創建用戶
db.createUser(
{
user: "springbucks",
pwd: "springbucks",
roles: [
{ role: "readWrite", db: "springbucks" }
]
} )
通過db.createUser來創建用戶,並指定用戶名密碼以及該用戶的權限。
查看用戶
show users
通過show users
命令可以看到當前數據庫中創建的賬號信息。
Spring對MongoDB的支持
Spring對MongoDB的支持是通過Spring Data MongoDB這個項目來支持的,Spring Data MongoDB提供了MongoTemplate來對數據做增刪改查的操作。Spring Data MongoDB和Spring Data JPA類似,有對應的注解來標識。
注解
- @Document
- @Id
MongoTemplate
- save / remove
- Criteria / Query / Update
使用MongoTemplate操作MongoDB
自定義類型轉化類
業務中使用了joda-money
這個類庫,我們在取數據的時候就需要將獲取到的數據類型Document
進行轉換成我們需要的類型Money
。
package com.lucky.spring.converter;
import org.bson.Document;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
/**
* Created by zhangdd on 2020/8/8
* <p>
* 處理將Document 轉換成 Money
*/
public class MoneyReaderConverter implements Converter<Document, Money> {
@Nullable
@Override
public Money convert(Document source) {
//取數據
Document money = (Document) source.get("money");
//從獲取的數據中取對應的字段
double amount = Double.parseDouble(money.getString("amount"));
String currency = ((Document) money.get("currency")).getString("code");
//將獲取的業務字段進行類型轉換
return Money.of(CurrencyUnit.of(currency), amount);
}
}
注入轉化Bean
類型轉換的功能已經做好,這里需要將該功能注入到容器中。
@Bean
public MongoCustomConversions mongoCustomConversions(){
return new MongoCustomConversions(Arrays.asList(new MoneyReaderConverter()));
}
說下思路吧。為什么會想到自定義這個轉換Bean。
既然Spring提供了MongoTemplate作為操作入口,那我們就從MongoTemplate開始看起。MongoTemplate同樣也是在自動配置模塊中,如下圖所示:
可以看到並排的包還有jpa、redis、couchbase等。
MongoDataAutoConfiguration
從左側類結構可以看出,MongoDataAutoConfiguration這個類方法不多。其中整個流程我們會用到的有如下這幾個
- mongoTemplate()
- mappingMongoConverter()
- mongoCustomConversions()
MongoTemplate
既然MongoTemplate是入口,那就先找到MongoTemplate這個Bean的聲明地方。該方法需要一個MongoConverter
。
@Bean
@ConditionalOnMissingBean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter converter) {
return new MongoTemplate(mongoDbFactory, converter);
}
MongoConverter
MongoConverter看名字像是和轉換有關的一個東西,如下圖是其類關系圖,可以看到其最后的實現類是MappingMongoConverter
這個類。
如MongoDataAutoConfiguration類中定義的Bean已經實現了MappingMongoConverter
這個類注入。
MappingMongoConverter
@Bean
@ConditionalOnMissingBean({MongoConverter.class})
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, MongoCustomConversions conversions) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
mappingConverter.setCustomConversions(conversions);
return mappingConverter;
}
在mappingMongoConverter方法定義中,需要一個MongoCustomConversions參數。
MongoCustomConversions
@Bean
@ConditionalOnMissingBean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Collections.emptyList());
}
可以看到在MongoCustomConversions 這個Bean的定義處,通過@ConditionalOnMissingBean
注解說明:如果我們不自己定義該Bean,就會使用這段代碼生成的Bean。同時這個方法里傳的是一個空集合。所以 為什么會想到自定義這個轉換Bean 的原理就在這里了。Spring中很多Bean都是留了這樣的一個入口讓我們去可以自定義Bean。
保存查詢數據
private void saveFindData() throws InterruptedException {
Coffee espresso = Coffee.builder()
.name("espresso")
.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
.createTime(new Date())
.updateTime(new Date())
.build();
mongoTemplate.save(espresso);
log.info("Coffee {}", espresso);
List<Coffee> list = mongoTemplate.find(
Query.query(Criteria.where("name").is("espresso")), Coffee.class
);
log.info("find {} coffee", list.size());
list.forEach(c->log.info("coffee {}",c));
TimeUnit.SECONDS.sleep(1000);
}
打印結果如下:
2020-08-08 09:56:59.163 INFO 50442 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:4}] to localhost:27017
2020-08-08 09:56:59.232 INFO 50442 --- [ main] com.lucky.spring.Application : Coffee Coffee(id=5f2e066b69ffe1c50ad58e1f, name=espresso, price=CNY 20.00, createTime=Sat Aug 08 09:56:59 CST 2020, updateTime=Sat Aug 08 09:56:59 CST 2020)
2020-08-08 09:56:59.277 INFO 50442 --- [ main] com.lucky.spring.Application : find 1 coffee
2020-08-08 09:56:59.278 INFO 50442 --- [ main] com.lucky.spring.Application : coffee Coffee(id=5f2e066b69ffe1c50ad58e1f, name=espresso, price=CNY 20.00, createTime=Sat Aug 08 09:56:59 CST 2020, updateTime=Sat Aug 08 09:56:59 CST 2020)
在MondoDB數據庫中查看結果:
更新數據
private void updateData() {
UpdateResult result = mongoTemplate.updateFirst(Query.query(Criteria.where("name").is("espresso")),
new Update().set("price", Money.ofMajor(CurrencyUnit.of("CNY"), 30)).currentDate("updateTime"),
Coffee.class);
log.info("update result:{}", result.getMatchedCount());
}
在MondoDB數據庫中查看結果: