Retrieving the specific MongoDB key from DuplicateKeyException that was actually duplicated (Java/Spring)
java, mongodb, spring, spring-data
Solution
Not really, as the Mongo Java Driver already exposes the last error as a constructed String:
`writeResult.getLastError().get("err")` returns something such as:
`insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.person.$username dup key: { : "joe" }`
This is also true for the shell and every driver, I imagine.
A reasonable solution, I think, is to parse such duplicate key exception using a custom exception:
public class DetailedDuplicateKeyException extends DuplicateKeyException {
public DetailedDuplicateKeyException(String msg) {
// Instead of just calling super parse the message here.
super(msg);
}
}
... a custom exception translator:
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);
}
}
... and setting the Spring configuration properly:
@Bean
public MongoFactoryBean mongo() {
MongoFactoryBean mongo = new MongoFactoryBean();
mongo.setExceptionTranslator(new DetailedDuplicateKeyExceptionTransaltor());
mongo.setHost("localhost");
return mongo;
}
EDIT
After inspecting `MongoTemplate` code (1.4.1.RELEASE), it seems that internally a `SimpleMongoDbFactory` is used to retrieve a default `MongoExceptionTranslator`, so the one created with `MongoFactoryBean` is shadowed. Had missed that part.
The solution is to override `SimpleMongoDbFactory` (forget about `MongoFactoryBean`, it's useless in this context):
public class MySimpleMongoDbFactory extends SimpleMongoDbFactory {
PersistenceExceptionTranslator translator = new
DetailedDuplicateKeyExceptionTransaltor();
public MySimpleMongoDbFactory(Mongo mongo, String databaseName) {
super(mongo, databaseName);
}
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return translator;
}
}
Now you can construct a template using the custom `MongoDbFactory`:
`template = new MongoTemplate (new MySimpleMongoDbFactory(new MongoClient(), "test"));`
Had tried, and this one works for me.
Problem
I am working with Spring-Data/MongoDB and am properly catching duplicate keys upon a save/insert. As an example, let's say I have a User being saved to a Collection. The User object is annotated with two `@Indexed(unique=true)` (two unique keys). Let's say they are 'email' and 'username'. How do I retrieve which index was actually duplicated during the insert process. The closest I get is when I execute this type of example code: ``` 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; } } ``` This prints the String: ``` { "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} ``` Without silly String manipulation or a Json conversion, is there a way to extract the `Collection.user.$username` via the Mongodriver API? I have been searching unsuccessfully.