我正在使用 java/morphia 来处理 mongodb。默认的 ObjectId 从 Java 层使用不是很方便。我想让它成为一个字符串类型,同时保持使用 ObjectId 的密钥生成过程,比如说_id = new ObjectId.toString()
。
我想知道这样做有没有副作用?例如,它会以任何方式影响数据库性能或导致密钥冲突吗?会不会影响分片环境...
您可以对字段使用任何类型的值_id
(数组除外)。如果您选择不使用 ObjectId,则必须以某种方式保证值的唯一性(将 ObjectId 转换为字符串即可)。如果您尝试插入重复键,则会发生错误,您将不得不处理它。
当您尝试将具有相同 _id 的两个文档插入不同的分片时,我不确定它会对分片集群产生什么影响。我怀疑它会让你插入,但这会在以后咬你。(我必须对此进行测试)。
也就是说,你应该没有问题_id = (new ObjectId).toString()
。
我实际上做了同样的事情,因为我在将 ObjectId 转换为 JSON 时遇到了一些问题。
然后我做了类似的事情
@Id
private String id;
public String getId() {
return id();
}
public void setId(String id) {
this.id = id;
}
一切正常,直到我决定更新以前插入的文档,当我通过 Id 获取对象时,通过 JSON 将其发送到页面并通过 JSON 发布接收相同的更新对象,然后使用数据存储中的保存功能,而不是更新之前的数据,它插入了一个新文档,而不是更新已经存在的数据。
更糟糕的是,新文档的 ID 与之前插入的 ID 相同,我认为这是不可能的。
无论如何,我将私有对象设置为 ObjectID,然后将 get 设置为字符串,然后它按预期工作,不确定这对您的情况有所帮助。
@Id
private ObjectId id;
public String getId() {
return id.toString();
}
public void setId(String id) {
this.id = new ObjectId(id);
}
是的,您可以使用字符串作为 _id。
只有当你有一些价值(在文档中)自然是一个很好的唯一键时,我才会推荐它。我在一个集合中使用了这种设计,其中有一个字符串地理标签,形式为“xxxxyyyy”;这个每个文档唯一的字段将必须在文档中,我必须在它上面建立一个索引......那么为什么不将它用作键呢?(这避免了一个额外的键值对,并且避免了集合上的第二个索引,因为 MongoDB 自然地在“_id”上构建了一个索引。鉴于集合的大小,这两者都大大节省了空间。)
但是,从您的问题的语气(“ObjectIDs 不是很方便”)来看,如果您想要使用字符串的唯一原因是您不想为弄清楚如何巧妙地管理 ObjectIDs 而烦恼......我' d 建议值得您花时间了解它们。我敢肯定他们不会有麻烦……一旦你发现了他们的麻烦。
否则:你有什么选择?以后每次使用 MongoDB 时,你都会编造字符串 ID 吗?
我想补充一点,使用自动生成的 BSON ObjectID 作为唯一标识符并不总是一个好主意,如果它被传递给应用程序:它可能会被用户操纵。
ObjectID 似乎是按顺序生成的,因此如果您未能实施必要的授权机制,恶意用户可以简单地增加他所拥有的值,以访问他不应该访问的资源。
更新:从版本 3.4+ 开始,不再以增量方式生成 ObjectID。请参阅3.2 文档与最新文档
因此,使用 UUID 类型标识符将提供一层安全性。当然,授权(是否允许此用户访问请求的资源)是必须的,但您应该了解上述 ObjectID 功能。
为了两全其美,请生成与您的 ObjectID 长度(12 或 24 个字符)匹配的 UUID,并使用它来创建您自己的 ObjectID 类型的 _id。
您也可以使用字符串作为 Java 的 ID。这是在 MongoDB 集合中插入一个具有字符串 ID 的对象的示例方法和相应的单元测试:
public Document insert(String json, String collectionName) {
MongoCollection<Document> collection = database.getCollection(collectionName);
BasicDBObject document = BasicDBObject.parse(json);
Document doc = new Document(document);
collection.insertOne(doc);
return doc;
}
@Test
void whenInsert_ShouldInsertOne() {
final String uuid = UUID.randomUUID().toString();
final String collection = "test_collection";
final Document doc = app.insert(String.format("{\"_id\":\"%s\", \"name\": \"test\"}",
uuid), collection);
assertThat(doc).isNotNull();
final String json = app.getById(uuid, collection);
assertThat(json).contains(String.format("\"_id\": \"%s\"", uuid));
}