MongoDB為Java提供了非常豐富的API操作,相比關系型數據庫,這種NoSQL本身的數據也有點面向對象的意思,所以對於Java來說,Mongo的數據結構更加友好。
MongoDB在今年做了一次重大升級,版本來到了3.0。
相比之前的版本,這個版本中又很大的變化,相應地,本文中的方法可能在舊的版本中無法使用。
安裝MongoDB Java Driver
使用maven的用戶在pom.xml中使用以下的dependency。
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.1.0-SNAPSHOT</version>
</dependency>
建立連接
程序可以通過MongoClient這個類和MongoDB數據庫建立連接。
MongoClient mongoClient = new MongoClient();
// or
MongoClient mongoClient = new MongoClient( "localhost" );
// or
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// or, 連接副本集,MongoClient會自動識別出
MongoClient mongoClient = new MongoClient(
Arrays.asList(new ServerAddress("localhost", 27017),
new ServerAddress("localhost", 27018),
new ServerAddress("localhost", 27019)));
MongoDatabase database = mongoClient.getDatabase("mydb");
如果mydb不存在的話,那么Mongo也會給我們新創建一個數據庫。
MongoDatabase的本質就是一個數據庫的連接,而MongoClient是一個Client,所以我們在應用中可能需要多個連接,卻只需要創建一個MongoClient就可以了,MongoClient本身封裝了一個連接池。關於MongoClient,后面再補一篇文章。
獲得集合
在得到連接之后,通過getCollection()方法來獲取,相同地,如果獲取的集合不存在,那么Mongo會為我們創建這個集合。
MongoCollection<Document> collection = database.getCollection("users");
插入一個文檔
在MongoDB中,數據都是以文檔的形式存在的,一個集合可以理解成一個“文檔鏈”。
在獲得集合之后,程序就可以通過集合來插入一個新的數據了。
在Mongo Java API中,文檔對應的類是Document , 文檔中間還可以內嵌文檔。
比如插入這樣的數據
{
"username" : "whthomas",
"age" : "22",
"location":{
"city" : "hangzhou",
"x" : 100,
"y" : 200
}
}
Document doc = new Document("username","whthomas").append("age", "22").append("location", new Document("city", "hangzhou").append("x", 100).append("y","200"));
collection.insertOne(doc);
如果要插入多個文檔。使用insertMany()函數效率會更高一些,這個函數接受一個List< Document >類型。
List<Document> documents = new ArrayList<Document>();
for (int i = 0; i < 100; i++) {
documents.add(new Document("i", i));
}
collection.insertMany(documents);
查詢操作
查詢操作數據庫操作中相對比較復雜的操作,在MongoDB中通過集合的find()方法來查詢數據。
find()函數會返回FindIterable,它提供了一個接口給程序操作和控制這個集合。
得到第一條數據
使用first()函數可以得到結果集中的第一條數據,如果沒有找到數據,則返回一個null值。
Document myDoc = collection.find().first();
得到所有數據
通過iterator方法將FindIterable對象轉換成一個MongoCursor對象。
MongoCursor<Document> cursor = collection.find().iterator();
try {
while (cursor.hasNext()) {
System.out.println(cursor.next().toJson());
}
} finally {
cursor.close();
}
條件查詢
MongoDB的Java條件查詢操作,在我看來有些不那么面向對象,寫起來有點函數式編程的味道。
通過Filters、Sorts和Projections三個類,我們可以完成復雜的查詢操作。
比如程序需要得到一個指定條件的數據
import static com.mongodb.client.model.Filters.*;
Document myDoc = collection.find(eq("i", 71)).first();
往find函數中“傳遞”一個eq函數,得到i為71的數據。
過濾條件函數
條件 | 函數 | 例子 |
---|---|---|
等於 | eq() | eq("i",50) |
大於 | gt() | gt("i",50) |
小於 | lt() | lt("i",50) |
大於等於(>=) | gte() | gte("i",50) |
小於等於(<=) | lte() | lte("i",50) |
存在 | exists() | exists("i") |
排序操作
對FindIterable對象使用sort函數進行排序。ascending函數表示升序,descending函數表示降序。
collection.find().sort(orderBy(ascending("x", "y"), descending("z")))
過濾字段
有時候,我們並不需要一條數據中所有的內容,只是需要一部分而已,mongoDB 提供了一個projection方法,解決了這個問題。
collection.find().projection(fields(include("x", "y"), excludeId()))
使用forEach
有時候對不同的集合會有相同的操作,做通用方法是最佳實踐,Mongo對於函數式的編程范式真是充滿了熱情,為我們提供了大量非常“函數式”的方法(在Java這種完全面向對象的語言里,做到這樣真是不容易)。
我們可以通過forEach函數和Block類完成對集合中每個節點數據的操作。
Block<Document> printBlock = new Block<Document>() {
@Override
public void apply(final Document document) {
System.out.println(document.toJson());
}
};
collection.find(gt("i", 50)).forEach(printBlock);
更新數據
使用updateOne()函數,更新一條數據,第一個參數選取需要被更新的記錄,第二個參數設置需要被更新的具體數據。
collection.updateOne(eq("i", 10), new Document("$set", new Document("i", 110)));
如果需要更新多條數據,可以使用updateMany函數,這個函數會返回一個UpdateResult類的對象,這個對象里面保存了數據更新的結果。
UpdateResult updateResult = collection.updateMany(lt("i", 100),
new Document("$inc", new Document("i", 100)));
刪除數據
通過集合使用deleteOne()方法來刪除指定的數據,如果想要刪除多條數據,使用deleteMany方法來完成操作.
collection.deleteOne(eq("i", 110));
DeleteResult deleteResult = collection.deleteMany(gte("i", 100));
Bulk操作
MongoDB提供了一種稱為Bulk的操作方式,數據不會被立即被持久化到數據庫中,而是等待程序調度,確定合適的時間持久化到數據庫中。
Bulk操作支持有序操作,和無序操作兩種模式。有序操作中數據的的操作,會按照順序操作,一旦發生錯誤,操作就會終止;而無序操作,則不安順序執行,只會報告哪些操作發生了錯誤。
Bulk操作支持增刪改三種操作,對應的model分別是InsertOneModel、UpdateOneModel、DeleteOneModel、ReplaceOneModel。它們都繼承於WriteModel
// 有序操作
collection.bulkWrite(
Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
new InsertOneModel<>(new Document("_id", 5)),
new InsertOneModel<>(new Document("_id", 6)),
new UpdateOneModel<>(new Document("_id", 1),
new Document("$set", new Document("x", 2))),
new DeleteOneModel<>(new Document("_id", 2)),
new ReplaceOneModel<>(new Document("_id", 3),
new Document("_id", 3).append("x", 4))));
// 無序操作
collection.bulkWrite(
Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
new InsertOneModel<>(new Document("_id", 5)),
new InsertOneModel<>(new Document("_id", 6)),
new UpdateOneModel<>(new Document("_id", 1),
new Document("$set", new Document("x", 2))),
new DeleteOneModel<>(new Document("_id", 2)),
new ReplaceOneModel<>(new Document("_id", 3),
new Document("_id", 3).append("x", 4))),
new BulkWriteOptions().ordered(false));