Edited at

JavaからMongoDBへのアクセス(接続、検索、insert、update、delete)

More than 5 years have passed since last update.

Java MongoDB Driverを使ってJavaからMongoDBにアクセスする方法について、全くはじめての人向けメモ。

MongoDBには色々機能があるようですが、今回はJava MongoDB Driverを使った簡単なCRUDの記述方法を解説します。


MongoDBのこと

RDBとは用語が異なります。

何も知らずにJava MongoDB Driverのドキュメント読んでも???となってしまうので、最低限、RDBとの用語の違いを把握しておきましょう。

RDBとMongoDBのデータベース構造の比較は以下の通り。

RDB
MongoDB

データベース
データベース

テーブル
コレクション

レコード
ドキュメント

また、MongoDBの特徴などは以下のようなサイトで把握します。


build.gradleの設定

build.gradleに以下の設定を追加します

dependencies {

...
compile group: 'org.mongodb', name:'mongo-java-driver', version:'2.12.3'
...
}

今回はGradleを利用しましたが、自分で依存関係を解決する場合はMongoDB Driversから適切なドライバをダウンロードしてパスを通します。


JavaからMongoDBにアクセス


サーバとの接続・切断

// MongoDBサーバに接続

MongoClient client = new MongoClient("localhost", 27017);
// 利用するDBを取得
DB db = mongoClient.getDB( "mydb" );

...

// サーバから切断
client.close();


  • new MongoClient()でコネクションプールのコネクションを1つをとってくる(らしい)

  • スレッドセーフで、複数のスレッドで共有できるよう設計されているので、1つのアプリで1つのインスタンスでOK(らしい)

  • getDB()したタイミングでデータベースがなければ勝手に生成される

詳しくはGetting Started with Java Driver参照。


認証

セキュアモード(注:Security Tutorials — MongoDB Manual 2.6.3)で起動している場合は認証が必要。

MongoClient mongoClient = new MongoClient();

DB db = mongoClient.getDB("mydb");
boolean auth = db.authenticate(myUserName, myPassword);


コレクション一覧取得

データベース内のコレクション一覧を取得

Set<String> colls = db.getCollectionNames();

for (String s : colls) {
System.out.println(s);
}


指定のコレクション取得

取得したコレクションに対してCRUD等の操作ができます。

DBCollection coll = db.getCollection("testCollection");


ドキュメントのInsert

取得したコレクションにドキュメントをインサートします。

以下の構造のドキュメントをインサートする場合、

{

"name" : "MongoDB",
"type" : "database",
"count" : 1,
"info" : {
"x" : 203,
"y" : 102
}
}

Javaのソースは以下になります。

BasicDBObject doc = new BasicDBObject("name", "MongoDB")

.append("type", "database")
.append("count", 1)
.append("info", new BasicDBObject("x", 203).append("y", 102));
coll.insert(doc);

BasicDBObject クラスを使ってデータ構造を作ります。

その後、インサート対象のコレクションのinsert()メソッドを呼び出すだけです。

が、はっきりいってDBObjectを作るのがとてもめんどくさい。。

JSON文字列からDBObjectをつくるには、以下のように記述します。

DBObject json = (DBObject) JSON.parse("{'name':'mkyong', 'age':30}");

coll.insert(json);


ドキュメントの検索


1件目のドキュメントを検索

コレクションから1件だけドキュメントを取得するには、以下のように記述します。

DBObject myDoc = coll.findOne();

System.out.println(myDoc);


カーソルを使って全てのドキュメントを取得

コレクションから全てのドキュメントを取得するにはfind()メソッドを利用します。

find()メソッドでは、クエリに一致した全てのドキュメントにアクセスできます。

DBCursor cursor = coll.find();

try {
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
} finally {
cursor.close();
}


ドキュメントの件数を取得する

コレクションに何件のドキュメントがあるか調べるにはgetCount()メソッドを利用します。

long count = coll.getCount();

System.out.println(count + "件");


クエリを使って1件のドキュメントを取得する

BasicDBObject query = new BasicDBObject("i", 71);

cursor = coll.find(query);

try {
while(cursor.hasNext()) {
System.out.println(cursor.next());
}
} finally {
cursor.close();
}


$オペレータを使ったクエリでドキュメントを検索する

MongoDBではクエリに$オペレータを利用して検索や集計等が行えます。

例えば、"qty"フィールドが20でないドキュメントを検索するには以下のように記述します

db.inventory.find( { qty: { $ne: 20 } } )

オペレータについての詳細はOperators — MongoDB Manual 2.6.3 参照。

$オペレータはDBObjectに、通常のStringキーとして設定します。

query = new BasicDBObject("name",                                   

new BasicDBObject("$ne", "mkyong")).append("count",
new BasicDBObject("$gt", 1));
cursor = coll.find(query);
try {
while (cursor.hasNext()) {
System.out.println("use $ operrator -> " + cursor.next());
}
} finally {
cursor.close();
}

でも、DBObjectを生成するのはやっぱり面倒なので、JSONからクエリを生成します。

json = (DBObject) JSON                                              

.parse("{'name':{$ne:'mkyong'}, 'count':{$gt:1}}");
cursor = coll.find(json);
try {
while (cursor.hasNext()) {
System.out.println("use $ ope(JSON) -> " + cursor.next());
}
} finally {
cursor.close();
}

coll.remove(new BasicDBObject());


ドキュメントUpdate

ドキュメントを更新するには、更新対象のDBObjectを取得して書き換え、save()メソッドで更新します。

このとき、_idは変更してはいけません。DBObjectの_idが一致するドキュメントがあった場合に上書きされるからです。

doc = coll.findOne();                              

doc.put("name", "MongoDB2"); // name属性を更新
doc.put("age", "15"); // age属性を追加
coll.save(doc);


ドキュメントDelete

ドキュメントを削除するには、削除対象のDBObjectを取得して、remove()メソッドで削除します。

doc = coll.findOne();

coll.remove(doc);


コレクション削除

コレクションを削除する場合drop()メソッドを利用します。

System.out.println(db.getCollectionNames()); 

coll.drop();
System.out.println(db.getCollectionNames());


サンプルコード

上記の処理まとめ。(認証無し)

MongoClient client = null;                                               

try {
// MondoDBサーバへの接続
client = new MongoClient("localhost", 27017);
// client = new MongoClient();
// 使用するDBの指定
DB db = client.getDB("mydb");

DBCollection coll = db.getCollection("testCollection");

// Insert(append版)
DBObject doc = new BasicDBObject("name", "MongoDB")
.append("type", "database")
.append("count", 1)
.append("info",
new BasicDBObject("x", 203).append("y", 102));
coll.insert(doc);

doc = new BasicDBObject("name", "MongoDB")
.append("type", "database")
.append("count", 2)
.append("info",
new BasicDBObject("x", 400).append("y", -10));
coll.insert(doc);

// Insert(JSON版)
DBObject json = (DBObject) JSON
.parse("{'name':'mkyong', 'age':30}");
coll.insert(json);

// 1件だけ検索
DBObject myDoc = coll.findOne();
System.out.println("findOne() -> " + myDoc);

// カーソルを使って全件取得
DBCursor cursor = coll.find();
try {
while (cursor.hasNext()) {
System.out.println("DBCursor - > " + cursor.next());
}
} finally {
cursor.close();
}

// ドキュメント件数取得
long count = coll.getCount();
System.out.println(count + "件");

// クエリを使って1件のドキュメントを取得する
BasicDBObject query = new BasicDBObject("name", "MongoDB");
cursor = coll.find(query);
try {
while (cursor.hasNext()) {
System.out.println("use query -> " + cursor.next());
}
} finally {
cursor.close();
}

// $オペレータを使ったクエリ
query = new BasicDBObject("name",
new BasicDBObject("$ne", "mkyong")).append("count",
new BasicDBObject("$gt", 1));
cursor = coll.find(query);
try {
while (cursor.hasNext()) {
System.out.println("use $ operator -> " + cursor.next());
}
} finally {
cursor.close();
}

// JSONからクエリを生成
json = (DBObject) JSON
.parse("{'name':{$ne:'mkyong'}, 'count':{$gt:1}}");
cursor = coll.find(json);
try {
while (cursor.hasNext()) {
System.out.println("use $ ope(JSON) -> " + cursor.next());
}
} finally {
cursor.close();
}

// ドキュメントをアップデート
doc = coll.findOne();
doc.put("name", "MongoDB2"); // name属性を更新
doc.put("age", "15"); // age属性を追加
coll.save(doc);
cursor = coll.find();
try {
while (cursor.hasNext()) {
System.out.println("updated -> " + cursor.next());
}
} finally {
cursor.close();
}

// ドキュメントを削除
doc = coll.findOne();
coll.remove(doc);
cursor = coll.find();
try {
while (cursor.hasNext()) {
System.out.println("deleted -> " + cursor.next());
}
} finally {
cursor.close();
}

// コレクション削除
System.out.println("コレクション削除前: " + db.getCollectionNames());
coll.drop();
System.out.println("コレクション削除後: " + db.getCollectionNames());

coll.remove(new BasicDBObject());

} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (client != null) {
client.close();
}
}


その他、気になったこと

上記以外に気になって調べたことのメモ。


トランザクション

MongoDBにはトランザクションがなく後勝ちなので、楽観ロックで排他制御するようです。

(4/4)触ってみよう!ビッグデータを支えるクラウド技術 - MongoDBを試してみよう:ITpro参照


(1)クエリーにより、ドキュメントを取り出す。

(2)古い値を保存しておく

(3)ドキュメントを更新

(4)古い値(_idを含む)を第1引数に、新しい更新したドキュメントを第2引数に指定して、updateメソッドを呼ぶ。ドキュメントが書き換わっていたらupdateが失敗する

(5) updateが失敗したら、また(1)からやり直す。


だそうです。

また、一連の操作中にエラーが起きてもその前の処理をロールバックできません。

db.getLastError();メソッドで最後に起こったエラーが確認できるようです。


save()とinsert()、update()の違い

save()メソッドと、insert()、update()メソッドが出てきて使い分けがよくわからなかったのでまとめ。

詳細はjavadoc参照。(DBCollection

save()
insert()
update()

更新できる件数
1件
1件以上
1件以上(*1)

対象の指定
ドキュメント
ドキュメント
クエリ

対象レコードの有無等
_idが無ければInsert、あればUpsert
-
指定したクエリを発行した時点で取得される_idに対応するドキュメントが、Update実行時コレクションに無い場合は更新失敗*2

*1:updateMulti()メソッドを利用するか、update()メソッドの第4引数にtrueセットで複数件更新。それ以外の場合は最初の1件だけ更新。

*2:update()メソッドの第3引数をtrueに設定するとUpsertされる


Bulkオペレーション


  • 大量のデータを操作する場合はBulkオペレーションを利用すると速いらしい

  • insert/update/removeが一括で処理できる

  • Buklオペレーションには2種類ある


    • Ordered bulk operation:順序を保証

    • Unordered bulk operations:順序を保証しない(並行処理)



StatementのaddBatch()、executeBatch()っぽい。

サンプルコードは以下の通り。

// 1. Ordered bulk operation

BulkWriteOperation builder = coll.initializeOrderedBulkOperation();
builder.insert(new BasicDBObject("_id", 1));
builder.insert(new BasicDBObject("_id", 2));
builder.insert(new BasicDBObject("_id", 3));

builder.find(new BasicDBObject("_id", 1)).updateOne(new BasicDBObject("$set", new BasicDBObject("x", 2)));
builder.find(new BasicDBObject("_id", 2)).removeOne();
builder.find(new BasicDBObject("_id", 3)).replaceOne(new BasicDBObject("_id", 3).append("x", 4));

BulkWriteResult result = builder.execute();

// 2. Unordered bulk operation - no guarantee of order of operation
builder = coll.initializeUnorderedBulkOperation();
builder.find(new BasicDBObject("_id", 1)).removeOne();
builder.find(new BasicDBObject("_id", 2)).removeOne();

result = builder.execute();