我正在使用Spring-Data / MongoDB,并在保存/插入时正确捕获重复的密钥。
举个例子,假设我有一个用户被保存到一个集合中。 User对象使用两个@Indexed(unique=true)
(两个唯一键)进行注释。假设它们是“电子邮件”和“用户名”。如何在插入过程中检索实际上重复的索引。
我最接近的是当我执行这种类型的示例代码时:
public boolean createNewUser() {
MongoTemplate operations = RepositoryFactory.getMongoOperationsInstance();
try {
log.debug("Saving new user to DB");
operations.save(this);
return true;
} catch (DuplicateKeyException dke) {
log.debug("User with same username or email found");
log.debug(operations.getDb().getLastError());
return false;
}
}
这将打印字符串:
{ "serverUsed" : "/127.0.0.1:27017" , "err" : "E11000 duplicate key error index: Collection.user.$username dup key: { : \"user\" }" , "code" : 11000 , "n" : 0 , "connectionId" : 17 , "ok" : 1.0}
如果没有愚蠢的字符串操作或Json转换,有没有办法通过Mongodriver API提取Collection.user.$username
?
我一直在搜索失败。
答案 0 :(得分:2)
不是真的,因为Mongo Java Driver已经将最后一个错误暴露为构造的String:
writeResult.getLastError().get("err")
会返回以下内容:
insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.person.$username dup key: { : "joe" }
我想,对于shell和每个驱动程序也是如此。
我认为合理的解决方案是使用自定义异常解析此类重复键异常:
public class DetailedDuplicateKeyException extends DuplicateKeyException {
public DetailedDuplicateKeyException(String msg) {
// Instead of just calling super parse the message here.
super(msg);
}
}
...自定义异常翻译器:
public class DetailedDuplicateKeyExceptionTransaltor extends MongoExceptionTranslator {
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof MongoException.DuplicateKey) {
return new DetailedDuplicateKeyException(ex.getMessage());
}
return super.translateExceptionIfPossible(ex);
}
}
...并正确设置Spring配置:
@Bean
public MongoFactoryBean mongo() {
MongoFactoryBean mongo = new MongoFactoryBean();
mongo.setExceptionTranslator(new DetailedDuplicateKeyExceptionTransaltor());
mongo.setHost("localhost");
return mongo;
}
在检查MongoTemplate
代码(1.4.1.RELEASE)后,似乎在内部SimpleMongoDbFactory
用于检索默认MongoExceptionTranslator
,因此使用MongoFactoryBean
创建的代码被遮蔽了。错过了那一部分。
解决方案是覆盖SimpleMongoDbFactory
(忘记MongoFactoryBean
,在此上下文中它无用):
public class MySimpleMongoDbFactory extends SimpleMongoDbFactory {
PersistenceExceptionTranslator translator = new
DetailedDuplicateKeyExceptionTransaltor();
public MySimpleMongoDbFactory(Mongo mongo, String databaseName) {
super(mongo, databaseName);
}
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return translator;
}
}
现在,您可以使用自定义MongoDbFactory
:
template = new MongoTemplate
(new MySimpleMongoDbFactory(new MongoClient(), "test"));
试过了,这个适合我。
答案 1 :(得分:0)
如果您在使用spring-data-rest
/ spring-data-mongodb
时遇到此问题,我编写了一个@ControllerAdvice
类,它使用@ExceptionHandler
方法以与验证相同的方式返回错误类。
我似乎没有在接受的答案中使用这些课程,这就是我发帖的原因。
我愿意接受更好的方法来解决这个问题(在Spring数据中)/实现这个@ExceptionHandler
。
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(DuplicateKeyException.class)
@ResponseStatus(value = HttpStatus.CONFLICT)
@ResponseBody
public Map<String, Object> handleDuplicateKeyException(DuplicateKeyException e) {
String entity = null;
String message = null;
String invalidValue = null;
String property = null;
String errorMessage = e.getMessage();
Pattern pattern = Pattern.compile("\\.(.*?) index: (.*?) dup key: \\{ : \\\\\"(.*?)\\\\\"");
Matcher matcher = pattern.matcher(errorMessage);
if (matcher.find()) {
entity = WordUtils.capitalize(matcher.group(1));
property = matcher.group(2);
invalidValue = matcher.group(3);
}
message = WordUtils.capitalize(property) + " must be unique";
Map<String, String> uniqueIndexViolation = new HashMap<>();
uniqueIndexViolation.put("entity", entity);
uniqueIndexViolation.put("message", message);
uniqueIndexViolation.put("invalidValue", invalidValue);
uniqueIndexViolation.put("property", property);
List<Object> errors = new ArrayList<Object>();
errors.add(uniqueIndexViolation);
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("errors", errors);
return responseBody;
}
}