Hello Konstantin,
I’ve done some research and some thinking dedicated to the problem. One of the interesting sources of thoughts was this forum https://forum.hibernate.org/viewtopic.php?f=1&t=928172&sid=c9bfdc41235c02dbd80f6fb63f2ec5bd
Basically the problematic invariant (contract, if you will) is the one corresponding to the part of the test you mentioned as not passing - Entity after persisting has to be equal to a “fork”(resereliazed entity) of this entity before persisting. Example (from the test):
IdentityEntity e1 = metadata.create(IdentityEntity.class);
// e1 & e3 are different instances with the same UUID
IdentityEntity e3 = TestSupport.reserialize(e1);
// one of them has an Id, other has not - this is the case when a newly committed instance returns from
// middleware to the client
idField.set(e3, 100L);
assertEquals(e1, e3);
assertTrue(e1.hashCode() == e3.hashCode());
I came to conclusion that there basically only two options here:
- give up this invariant - entities may not be equal in this scenario
- implement HasUuid and in the way, that uuid is actually persisted in the DB
The hacky solution in IdProxy where equals for a persisted entity is implemented in the way that it uses two different implementations of comparing depending on what the entity is compared to - if the second entity has only uuid compare only those, if the second has generated id as well compare those. This hacky solution is good enough for equals, but as in hashCode there is no way of knowing to what the generated hashCode is going to be compared to, this hack cannot be applied to it (they came to a similar problem in the aforementioned topic on hibernate forum). As a second problem I see that this implementation of equals is not transitive - however improbable the scenario is.
Conclusion for us - and maybe for you and the docs - use BaseUuidEntity. It implements HasUuid in the right way and does not store any other redundant primary keys. Any other implementation will either have two different primary keys or its hashCode is not gonna work well and big performance issues may be expected. Understanding all this, I think I would be happier if CUBA did not let me use anything else then BaseUuidEntity. Our next project based on CUBA is definitely going to be built with BaseUuidEntity.
Another big advantage of using BaseUuidEntityis avoiding the requirement to retrieve IDs from DB after every insert. That way inserts can be further optimized using jdbc.batch-writing | EclipseLink 2.5.x Java Persistence API (JPA) Extensions Reference If IDENTITY strategy is used this property is ignored as ID is retrieved after every insert.
In the meantime we are testing our version of IdProxy that breaks the invariant and everything seems to be working, but when we come to a first problem with the invariant we are going to redo our entities to make them BaseUuidEntity - another reason for this is Entity Log and similar framework features - btw. when do you think the 6.5 is going to be released?
I attached our version of IdProxy. There is another performance issue that I think I fixed in IdProxy and that is method copy creating new instances all over. That is IMHO not required and I attempted to fix that. That has nothing to do with the invariant and you can safely adopt, if you want.
Sorry for a delay and really long comment. Thank you for your time,
IdProxy2.java (4.9K)