We're updating the issue view to help you get more done. 

PersistentSortedSet<String> with @SortComparator(CaseInsensitiveOrdering.class) using a join table causes a constraint violation if you delete an entry and then re-add a value that differs from the previous value only by case

Description

I have the following class:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 @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; } }

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

When I attempt to commit this, I get a constraint violation and my transaction rolls back, because Envers is trying to add a DEL and ADD record to ec_resource_pool_resource_aud for the same revision and resource pool id that differ only by the case of the resource name string value, but the primary key index on ec_resource_pool_resource (of the three columns rev, the id, and the resource name) is case independent, so it considers these two primary key values to be identical.

This problem 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.

Environment

None

Status

Assignee

Unassigned

Reporter

Roger Dearnaley

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Priority

Major