CollectionDatasource.containsItem(id), getItem(id) with constructed IdProxy objects fail to find items

Background:
sometimes there’s the need to store the raw ID of some entities (Numeric based ones), and later on find the corresponding entity in an existing CollectionDatasource.

So you basically end up with the following code (more or less):

IdProxy<Integer> id = IdProxy.of(event.getRawId());
Entity actualEntity = collectionDs.getItem(id);

// uh oh!! actualEntity is null!

But the problem with the current implementation in the CollectionDatasourceImpl class is that it relies on the AbstractHashedMap#get method, that happens to find its elements only by the exact key’s hash, and that’s fine most of the time.
Unfortunately it’s not good for constructed IdProxy objects, because they have a different hash value than the one by which the original entity has been stored with.
So every method that relies on the underlying LinkedMap#get method fails to find entities when you pass “constructed” id proxies, like in the above example.

I don’t know if this is also an issue with the new 7.0 APIs, but that’s something that should be addressed IMHO.

Thx
Paolo

UPDATE:

I can confirm that the problem is how the IdProxy#hashCode is computed.

Specifically, if constructed with an entity that implements HasUuid, the hashCode uses the uuid's hashCode, otherwise it defaults to 0:

    IdProxy(BaseDbGeneratedIdEntity<T> entity) {
        Preconditions.checkNotNullArgument(entity, "entity is null");
        this.entity = entity;
        if (entity instanceof HasUuid) {
            this.uuid = ((HasUuid) entity).getUuid();
            if (this.uuid == null)
                throw new IllegalStateException("Entity " + entity.getClass() + " implements HasUuid but its instance has no UUID assigned");
            this.hashCode = this.uuid.hashCode();
        } else {
            this.uuid = UuidProvider.createUuid();
            this.hashCode = 0; // trade-off, see com.haulmont.cuba.primary_keys.IdentityTest.testEquality
        }
    }

While this makes sense if you think of an IdProxy like a “whole” between the id and its related entity, it doesn’t if you need it only as a “container” of the underlying id (and nothing more).

In the latter case, the hashCode should be computed only by using the value of the wrapped id, and ignoring whatever entity is linked to the proxy. In the actual implementation this class violates SRP, that is it’s not a simple “ID proxy”, but a link between the id and the entity, and as such it’s not usable where you need only an ID proxy…

Hi Paolo,

Thank you for the investigation. I’ve filed an issue.