I have the following class:
{code} @Audited @DynamicInsert @DynamicUpdate @Entity(name = "ResourcePool") @Proxy(proxyClass = ResourcePool.class) @Table( name = "ec_resource_pool", uniqueConstraints = @UniqueConstraint( name = "iu_resource_pool_name", columnNames = "name" ) ) public class ResourcePoolImpl extends AbstractProtectedPropertySheetOwner implements ResourcePool { ... private UUID m_id; private SortedSet<String> m_resourceNames; ... @Override public boolean addResourceName( @NonNls @NotNull String resourceName) {
if (m_resourceNames == null) { m_resourceNames = CollectionUtil.createCaseInsensitiveSet(); }
return m_resourceNames.add(resourceName); } ... @Column( length = UUID_LENGTH, updatable = false, nullable = false ) @DocumentId @GeneratedValue(generator = "ecid") @GenericGenerator( name = "ecid", strategy = "com.electriccloud.dao.EntityIdentifierGenerator" ) @Id @NonNls @NotNull @Override public UUID getId() { return m_id; } ... @AuditJoinTable(name = "ec_resource_pool_resource_aud") @Column( name = "resource_name", length = MAX_NAME_LENGTH ) @ElementCollection(fetch = LAZY) @JoinTable( name = "ec_resource_pool_resource", joinColumns = @JoinColumn( name = "resource_pool_id", foreignKey = @ForeignKey(name = "fk_resource_pool") ) ) @Nullable @Override @SortComparator(CaseInsensitiveOrdering.class) public SortedSet<String> getResourceNames() { return m_resourceNames; } ... @Override public boolean removeResourceName( @NonNls @NotNull String resourceName) { return m_resourceNames != null && m_resourceNames.remove(resourceName); } ... @Override public void setId(@NonNls @Nullable UUID id) { m_id = id; } ... @Override public void setResourceNames(SortedSet<String> resourceNames) { m_resourceNames = resourceNames; } } {code}
The join table ec_resource_pool_resource and the audit table ec_resource_pool_resource_aud both have case-insensitive collation order.
I create a resource pool whose m_resourceNames with some id which contains the entry 'foo', which produces a row in ec_resource_pool_resource with that id and resource_name = 'foo'
I then do:
resourcePool.remove("foo"); resourcePool.add("FOO"); // Differs from the previous value only by case
Once this is committed, When I find I have TWO rows in ec_resource_pool_resource with the same id one with resource_name = 'FOO' (as expected) but the original one with resource_name = 'foo' -- which should have been deleted, but was not.
(NOTE -- If you have auditing turned on, you won't be able attempt to get commit this far , due to I get a constraint violation issue which I will file and my transaction rolls back, because Envers is trying to add a separate JIRA DEL and ADD record to ec_resource_pool_resource_aud for .)
If I make further changes the same revision and resource pool id that differ only by the case (e.g. tothe of the resource 'Foo') name string value , then I get even more duplicated rows in but the primary key index on ec_resource_pool_resource .
This problem does NOT occur if I put a flush between (of the delete three columns rev, the id, and the add resource name) is case independent, so it considers these two primary key values to be identical .
This problem has existed at least since didn't happen back in Hibernate 4.3.x.
I haven't yet turned this into a test case -- hopefully I'll be able to do so fairly soon. |
|