Skip to content

IDs with non-autogeneratable type cannot be assigned automatically with custom event listeners [DATAMONGO-1617] #2481

Closed
@spring-projects-issues

Description

@spring-projects-issues

László Csontos opened DATAMONGO-1617 and commented

Under normal circumstances the MongoDB driver generates a unique ID for objects to be persisted. The default set of types for which this works out-of-the-box are enumerated here: org.springframework.data.mongodb.core.mapping.MongoSimpleTypes.AUTOGENERATED_ID_TYPES, that is, ObjectId, String and BigInteger.

Let's consider the following example:

public class Customer implements Persistable<UUID> {

    @Id
    private UUID id;
    ...

    public UUID getId() { return id; }
    public void setId(UUID id) { this.id = id; }
    public boolean isNew() { return (getId() == null); }
   ...

When an instance of this object is saved, it already has to contain an UUID instance otherwise the following exception occurs:

Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Cannot autogenerate id of type java.util.UUID for entity of type hello.Customer!
	at org.springframework.data.mongodb.core.MongoTemplate.assertUpdateableIdIfNotSet(MongoTemplate.java:1304) ~[spring-data-mongodb-1.10.0.RELEASE.jar:na]
	at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:845) ~[spring-data-mongodb-1.10.0.RELEASE.jar:na]
	at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:793) ~[spring-data-mongodb-1.10.0.RELEASE.jar:na]
	at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:80) ~[spring-data-mongodb-1.10.0.RELEASE.jar:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]

Even if someone tries to define a custom entity listener to generate UUIDs for entities automatically, it's not going to work due to the way how MongoTemplate is implemented.

@Component
public class GenerateUUIDListener extends AbstractMongoEventListener<Customer> {

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Customer> event) {
        Customer customer = event.getSource();
        if (customer.isNew()) {
            customer.setId(UUID.randomUUID());
        }
    }

}

The following code was taken from MongoTemplate.doInsert()_.

	protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {

		assertUpdateableIdIfNotSet(objectToSave);

		initializeVersionProperty(objectToSave);

		maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));

		DBObject dbDoc = toDbObject(objectToSave, writer);

		maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc, collectionName));
		Object id = insertDBObject(collectionName, dbDoc, objectToSave.getClass());

		populateIdIfNecessary(objectToSave, id);
		maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc, collectionName));
	}

Here the problem is that assertUpdateableIdIfNotSet(objectToSave) is called before emitting BeforeConvertEvent and this way entity listeners don't have the chance to populate custom ID fields.

My suggestion is that emitting the BeforeConvertEvent could come before checking the ID and the setting the version fields to fix this.

What do you think about this approach?


Affects: 1.10 GA (Ingalls)

Reference URL: https://github.com/springuni/gs-accessing-data-mongodb/commits/DATAMONGO-1617

Referenced from: pull request #443

Backported to: 1.10.1 (Ingalls SR1), 1.9.8 (Hopper SR8)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions