我有一个MongoDB集合,我存储User
这样的文档:
{
"_id" : ObjectId("52d14842ed0000ed0017cceb"),
"email": "joe@gmail.com",
"firstName": "Joe"
...
}
用户必须是唯一的电子邮件地址,因此我添加了email
字段的索引:
collection.indexesManager.ensure(
Index(List("email" -> IndexType.Ascending), unique = true)
)
以下是我插入新文档的方式:
def insert(user: User): Future[User] = {
val json = user.asJson.transform(generateId andThen copyKey(publicIdPath, privateIdPath) andThen publicIdPath.json.prune).get
collection.insert(json).map { lastError =>
User(json.transform(copyKey(privateIdPath, publicIdPath) andThen privateIdPath.json.prune).get).get
}.recover {
throw new IllegalArgumentException(s"an user with email ${user.email} already exists")
}
}
如果出现错误,上面的代码会抛出IllegalArgumentException
,并且调用者可以相应地处理它。但是,如果我像这样修改recover
部分......
def insert(user: User): Future[User] = {
val json = user.asJson.transform(generateId andThen copyKey(publicIdPath, privateIdPath) andThen publicIdPath.json.prune).get
collection.insert(json).map { lastError =>
User(json.transform(copyKey(privateIdPath, publicIdPath) andThen privateIdPath.json.prune).get).get
}.recover {
case e: Throwable => throw new IllegalArgumentException(s"an user with email ${user.email} already exists")
}
}
...我不再得到IllegalArgumentException
,但我得到的是这样的:
play.api.Application$$anon$1: Execution exception[[IllegalArgumentException: DatabaseException['E11000 duplicate key error index: gokillo.users.$email_1 dup key: { : "giuseppe.greco@agamura.com" }' (code = 11000)]]]
...并且调用者不再能够处理异常。现在真正的问题是:
LastError
部分处理各种错误类型(即recover
提供的错误类型?)IllegalArgumentException
)?答案 0 :(得分:2)
最后我能够正确地管理事情。下面是如何插入用户并使用ReactiveMongo处理可能的异常:
val idPath = __ \ 'id
val oidPath = __ \ '_id
/**
* Generates a BSON object id.
*/
protected val generateId = __.json.update(
(oidPath \ '$oid).json.put(JsString(BSONObjectID.generate.stringify))
)
/**
* Converts the current JSON into an internal representation to be used
* to interact with Mongo.
*/
protected val toInternal = (__.json.update((oidPath \ '$oid).json.copyFrom(idPath.json.pick))
andThen idPath.json.prune
)
/**
* Converts the current JSON into an external representation to be used
* to interact with the rest of the world.
*/
protected val toExternal = (__.json.update(idPath.json.copyFrom((oidPath \ '$oid).json.pick))
andThen oidPath.json.prune
)
...
def insert(user: User): Future[User] = {
val json = user.asJson.transform(idPath.json.prune andThen generateId).get
collection.insert(json).transform(
success => User(json.transform(toExternal).get).get,
failure => DaoServiceException(failure.getMessage)
)
}
user
参数是一个类似POJO的实例,在JSON中有一个内部表示 - User
实例总是包含有效的JSON,因为它是在构造函数中生成和验证的,我不再需要检查是否user.asJson.transform
失败。
第一个transform
确保id
中已有user
,然后生成全新的Mongo ObjectID。然后,将新对象插入数据库中,最后将结果转换回外部表示(即_id
=> id
)。如果失败,我只是使用当前错误消息创建自定义异常。我希望有所帮助。
答案 1 :(得分:1)
我的经验更多的是使用纯Java驱动程序,因此我只能评论您使用mongo的策略 -
在我看来,你事先通过查询完成的所有操作都是复制mongos唯一性检查。即便如此,由于可能的失败,您仍然必须向上渗透异常。这不仅速度较慢,而且容易受到竞争条件的影响,因为查询+插入的组合不是原子的。在那种情况下,你有
如果发生这种情况,让mongo抛出db异常并抛出你自己的非法参数会不会更简单?
另外,如果省略它,请确保为您生成id,并且如果您仍然希望对其进行编码,则可以使用更简单的查询进行唯一性检查。至少在java驱动程序中你可以做到
collection.findOne(new BasicDBObject("email",someemailAddress))
答案 2 :(得分:0)
查看更新方法的upsert模式(“如果没有匹配存在则插入新文档(Upsert)”部分):http://docs.mongodb.org/manual/reference/method/db.collection.update/#insert-a-new-document-if-no-match-exists-upsert
答案 3 :(得分:0)
我在反应动物的谷歌小组回复了一段类似的问题。您可以在恢复块中包含另一个案例以匹配LastError对象,查询其错误代码并适当地处理错误。这是最初的问题: