Java MongoDB Driverを使ってJavaからMongoDBにアクセスする方法について、全くはじめての人向けメモ。
MongoDBには色々機能があるようですが、今回はJava MongoDB Driverを使った簡単なCRUDの記述方法を解説します。
#MongoDBのこと
RDBとは用語が異なります。
何も知らずにJava MongoDB Driverのドキュメント読んでも???となってしまうので、最低限、RDBとの用語の違いを把握しておきましょう。
RDBとMongoDBのデータベース構造の比較は以下の通り。
RDB | MongoDB |
---|---|
データベース | データベース |
テーブル | コレクション |
レコード | ドキュメント |
また、MongoDBの特徴などは以下のようなサイトで把握します。
- Introduction to MongoDB — MongoDB Manual 2.6.3
- 触ってみよう!ビッグデータを支えるクラウド技術 - MongoDBを試してみよう:ITpro
- MongoDBでゆるふわDB体験:連載|gihyo.jp … 技術評論社
#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](http://api.mongodb.org/java/current/com/mongodb/DBCollection.html#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)))
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();