我不确定从哪里开始或哪些信息相关,请告诉我哪些其他信息可能有助于解决此问题。
我正在开发一个简单的cometd应用程序,我正在使用mongodb作为我的存储后端。我在应用程序启动时获取了一个mongodb实例,并将此实例用于所有查询。事实上,这是由mongo java驱动程序文档推荐的,如下所述:http://www.mongodb.org/display/DOCS/Java+Driver+Concurrency。我正在抓住吸管认为这个问题与线程安全有关,但根据该链接,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);
}
}
接下来两个扩展MyBasicDBObject
的骨架类。
public class FirstDBObject extends MyBasicDBObject { //no code }
public class SecondDBObject extends MyBasicDBObject { //no code }
我以这种方式设置类的唯一原因是为了提高在同一范围内处理这两个对象的代码可读性。这让我可以做以下......
//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 shell调查问题时,我看到first
已在数据库和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直接交互,而不是使用某种类型的库来让我的生活更轻松。我在这个问题上做了一个傻瓜,但希望有人能从我的错误中吸取教训并为自己节省很多挫折。
感谢大家的帮助。
答案 0 :(得分:2)
很容易成为多线程问题。如果只有一个Mongo实例,那么Mongo,DB和DBCollection对象是线程安全的是正确的,而DBObjects 不线程安全。但即使它们是线程安全的,你的updateMapObjectsFoo / Bar方法也不会确保它们是数据库上的原子操作。
不幸的是,您需要对代码进行的更改比仅仅添加一些“同步”关键字更为激烈。看看http://www.mongodb.org/display/DOCS/Atomic+Operations是否无法帮助您了解问题的范围和一些可能的解决方案。