どこから始めればよいか、どの情報が関連しているかわかりません。この問題の解決に役立つ可能性のある追加情報を教えてください。
私は単純な cometd アプリケーションを開発しており、ストレージ バックエンドとして mongodb を使用しています。アプリケーションの起動時に単一の mongodb インスタンスを取得し、このインスタンスをすべてのクエリに使用します。これは、実際には、http://www.mongodb.org/display/DOCS/Java+Driver+Concurrency に記載されているように、mongo Java ドライバーのドキュメントで推奨されています。問題はスレッドセーフに関係していると考えてストローをつかんでいましたが、そのリンクによると、mongodbは完全にスレッドセーフです。
ここが興味深いところです。を拡張するクラスがありますBasicDBObject
。
public class MyBasicDBObject {
private static final String MAP = "map";
public boolean updateMapAnd(String submap, String key, byte[] value) {
Map topMap = (Map)this.get(MAP);
Map embeddedMap = topMap.get(submap);
byte[] oldValue = embeddedMap.get(key);
newValue = UtilityClass.binaryAnd(oldValue, value);
embeddedMap.put(key, newValue);
topMap.put(submap, embeddedMap);
this.put(MAP, topMap);
}
public boolean updateMapXor(String submap, String key, byte[] value) {
Map topMap = (Map)this.get(MAP);
Map embeddedMap = topMap.get(submap);
byte[] oldValue = embeddedMap.get(key);
newValue = UtilityClass.binaryXor(oldValue, value);
embeddedMap.put(key, newValue);
topMap.put(submap, embeddedMap);
this.put(MAP, topMap);
}
}
を拡張する次の 2 つのスケルトン クラスMyBasicDBObject
。
public class FirstDBObject extends MyBasicDBObject { //no code }
public class SecondDBObject extends MyBasicDBObject { //no code }
このようにクラスを設定した唯一の理由は、同じスコープ内でこれら 2 つのオブジェクトを処理する際にコードを読みやすくするためです。これにより、次のことができます...
//a cometd service callback
public void updateMapObjectsFoo(ServerSession remote, Message message) {
//locate the objects to update...
FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... });
SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... });
//update them as follows
first.updateMapAnd("default", "someKey1", newBinaryData1);
second.updateMapAnd("default", "someKey2", newBinaryData2);
//save (update) them to their respective collections
firstCollection.save(first);
secondCollection.save(second);
}
public void updateMapObjectsBar(ServerSession remote, Message message) {
//locate the objects to update...
FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... });
SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... });
/**
* the only difference is these two calls
*/
first.updateMapXor("default", "someKey1", newBinaryData1);
second.updateMapXor("default", "someKey2", newBinaryData2);
//save (update) them to their respective collections
firstCollection.save(first);
secondCollection.save(second);
}
渡されたバイト配列を反復処理することによりUtilityClass
、メソッドの名前、ビット単位、およびビット単位とまったく同じように動作します&
。^
これは私が完全に迷っているところです。 updateMapObjectsFoo()
期待どおりに機能し、データベースの変更first
をsecond
反映します。 updateMapObjectsBar()
一方、適切に更新することしかできませんfirst
。
デバッグによる検査updateMapObjectsBar()
では、バイナリ オブジェクトが実際には両方のオブジェクトで適切に更新されていることが示されていますが、問題を調査するために mongo シェルに向かうとfirst
、DB で更新されていて更新されてsecond
いないことがわかります。スレッドセーフがそれと関係があるという考えはどこから得たのでしょうか? 私を悩ませている唯一の違いは、それsecondCollection
が他の cometd サービスで使用されているのに使用されてfirstCollection
いないことです。それは一方では関連しているように見えますが、機能し、機能しないため、他方ではそうではFoo
ありBar
ません。
コードを分解して元に戻しましたが、この同じ問題に何度も戻ってきます。ここで何が起こっているのですか?
Javaジェネリックの悪夢と、言語のこの機能へのmongodbドライバーの依存である、すべての中で最も関連性のある部分を省略したようです。 BasicDBObject
は本質的に のラッパーですMap<String, Object>
。問題は、オブジェクトをそのマップに格納すると、そこに配置したときの状態に戻す必要があることです。はい、それは完全に明白に思えるかもしれません。この質問を投稿する前に、私はそれを十分に知っていました。
何が起こったのかを正確に特定することはできませんが、Java + mongodb ユーザーにこのアドバイスを提供します。多くのキャストを行い、データ構造が複雑になればなるほど、より多くのキャストが必要になります。簡単に言うと、次のことはしないでください。
DBObject obj = (DBObject) collection.findOne(new BasicDBObject("_id", new ObjectId((String)anotherObj.get("objId"))));
ラピッド プロトタイプを作成しているときは、ワンライナーは魅力的ですが、それを何度もやり始めると間違いを犯すことになります。今より多くのコードを書いて、後でフラストレーションを感じないようにしましょう:
DBObject query = new DBObject();
String objId = (String) anotherObj.get("objId");
query.put("_id", new ObjectId(objId));
obj = (DBObject) collection.findOne(query);
これは煩わしいほど冗長だと思いますが、ある種のライブラリを使用する代わりに、Mongo と直接対話することで、生活が楽になることを期待する必要があります。私はこれで自分をばかにしてしまいましたが、誰かが私の過ちから学び、多くのフラストレーションを救ってくれることを願っています.
ご協力いただきありがとうございます。