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.

Original source

Related problems