Skip to content

Commit 120237e

Browse files
kristofferlundberglukasz-antoniak
authored andcommitted
HHH-8171 - SETORDINAL to support set of embeddables
1 parent 06e6c04 commit 120237e

File tree

9 files changed

+129
-30
lines changed

9 files changed

+129
-30
lines changed

documentation/src/main/docbook/devguide/en-US/Envers.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,17 @@
315315
For example: a property called "age", will by default get modified flag with column name "age_MOD".
316316
</entry>
317317
</row>
318+
<row>
319+
<entry>
320+
<property>org.hibernate.envers.embeddable_set_ordinal_field_name</property>
321+
</entry>
322+
<entry>
323+
SETORDINAL
324+
</entry>
325+
<entry>
326+
The name of the column used for storing the ordinal of the change in sets of embeddables.
327+
</entry>
328+
</row>
318329
</tbody>
319330
</tgroup>
320331
</table>

hibernate-envers/src/main/java/org/hibernate/envers/configuration/AuditEntitiesConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public class AuditEntitiesConfiguration {
5757

5858
private final boolean revisionEndTimestampEnabled;
5959
private final String revisionEndTimestampFieldName;
60+
61+
private final String embeddableSetOrdinalPropertyName;
6062

6163
public AuditEntitiesConfiguration(Properties properties, String revisionInfoEntityName) {
6264
this.revisionInfoEntityName = revisionInfoEntityName;
@@ -112,6 +114,10 @@ public AuditEntitiesConfiguration(Properties properties, String revisionInfoEnti
112114

113115
revisionNumberPath = originalIdPropName + "." + revisionFieldName + ".id";
114116
revisionPropBasePath = originalIdPropName + "." + revisionFieldName + ".";
117+
118+
embeddableSetOrdinalPropertyName = getProperty( properties,
119+
"org.hibernate.envers.embeddable_set_ordinal_field_name",
120+
"org.hibernate.envers.embeddable_set_ordinal_field_name", "SETORDINAL" );
115121
}
116122

117123
public String getOriginalIdPropName() {
@@ -182,4 +188,8 @@ public String getAuditStrategyName() {
182188
public String getRevisionEndFieldName() {
183189
return revisionEndFieldName;
184190
}
191+
192+
public String getEmbeddableSetOrdinalPropertyName() {
193+
return embeddableSetOrdinalPropertyName;
194+
}
185195
}

hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
import org.hibernate.mapping.OneToMany;
8888
import org.hibernate.mapping.PersistentClass;
8989
import org.hibernate.mapping.Property;
90+
import org.hibernate.mapping.SimpleValue;
9091
import org.hibernate.mapping.Table;
9192
import org.hibernate.mapping.Value;
9293
import org.hibernate.type.BagType;
@@ -505,6 +506,22 @@ private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMappin
505506
);
506507
}
507508

509+
// Add an additional column holding a number to make each entry unique within the set,
510+
// since embeddable properties can be null
511+
if ( propertyValue.getCollectionType() instanceof SetType ) {
512+
final String auditedEmbeddableSetOrdinalPropertyName = mainGenerator.getVerEntCfg()
513+
.getEmbeddableSetOrdinalPropertyName();
514+
final String auditedEmbeddableSetOrdinalPropertyType = "integer";
515+
516+
final SimpleValue simpleValue = new SimpleValue( component.getMappings(), component.getTable() );
517+
simpleValue.setTypeName( auditedEmbeddableSetOrdinalPropertyType );
518+
519+
final Element idProperty = MetadataTools.addProperty( xmlMapping,
520+
auditedEmbeddableSetOrdinalPropertyName, auditedEmbeddableSetOrdinalPropertyType, true, true );
521+
MetadataTools.addColumn( idProperty, auditedEmbeddableSetOrdinalPropertyName, null, 0, 0, null, null,
522+
null, false );
523+
}
524+
508525
return new MiddleComponentData( componentMapper, 0 );
509526
} else {
510527
// Last but one parameter: collection components are always insertable
@@ -528,14 +545,13 @@ private void addMapper(CommonCollectionMapperData commonCollectionMapperData, Mi
528545
Type type = propertyValue.getType();
529546
boolean embeddableElementType = isEmbeddableElementType();
530547
if (type instanceof SortedSetType) {
531-
currentMapper.addComposite(propertyAuditingData.getPropertyData(),
532-
new SortedSetCollectionMapper(commonCollectionMapperData,
533-
TreeSet.class, SortedSetProxy.class, elementComponentData, propertyValue.getComparator(),
534-
embeddableElementType));
548+
currentMapper.addComposite( propertyAuditingData.getPropertyData(), new SortedSetCollectionMapper(
549+
commonCollectionMapperData, TreeSet.class, SortedSetProxy.class, elementComponentData,
550+
propertyValue.getComparator(), embeddableElementType, embeddableElementType ) );
535551
} else if (type instanceof SetType) {
536-
currentMapper.addComposite(propertyAuditingData.getPropertyData(),
537-
new BasicCollectionMapper<Set>(commonCollectionMapperData,
538-
HashSet.class, SetProxy.class, elementComponentData, embeddableElementType));
552+
currentMapper.addComposite( propertyAuditingData.getPropertyData(), new BasicCollectionMapper<Set>(
553+
commonCollectionMapperData, HashSet.class, SetProxy.class, elementComponentData,
554+
embeddableElementType, embeddableElementType ) );
539555
} else if (type instanceof SortedMapType) {
540556
// Indexed collection, so <code>indexComponentData</code> is not null.
541557
currentMapper.addComposite(propertyAuditingData.getPropertyData(),
@@ -548,9 +564,9 @@ private void addMapper(CommonCollectionMapperData commonCollectionMapperData, Mi
548564
new MapCollectionMapper<Map>(commonCollectionMapperData,
549565
HashMap.class, MapProxy.class, elementComponentData, indexComponentData, embeddableElementType));
550566
} else if (type instanceof BagType) {
551-
currentMapper.addComposite(propertyAuditingData.getPropertyData(),
552-
new BasicCollectionMapper<List>(commonCollectionMapperData,
553-
ArrayList.class, ListProxy.class, elementComponentData, embeddableElementType));
567+
currentMapper.addComposite( propertyAuditingData.getPropertyData(), new BasicCollectionMapper<List>(
568+
commonCollectionMapperData, ArrayList.class, ListProxy.class, elementComponentData,
569+
embeddableElementType, embeddableElementType ) );
554570
} else if (type instanceof ListType) {
555571
// Indexed collection, so <code>indexComponentData</code> is not null.
556572
currentMapper.addComposite(propertyAuditingData.getPropertyData(),

hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractCollectionMapper.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,17 @@
5555
public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
5656
protected final CommonCollectionMapperData commonCollectionMapperData;
5757
protected final Class<? extends T> collectionClass;
58+
protected final boolean ordinalInId;
5859
protected final boolean revisionTypeInId;
5960

6061
private final Constructor<? extends T> proxyConstructor;
6162

62-
protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
63-
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
64-
boolean revisionTypeInId) {
63+
protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
64+
Class<? extends T> collectionClass, Class<? extends T> proxyClass, boolean ordinalInId,
65+
boolean revisionTypeInId) {
6566
this.commonCollectionMapperData = commonCollectionMapperData;
6667
this.collectionClass = collectionClass;
68+
this.ordinalInId = ordinalInId;
6769
this.revisionTypeInId = revisionTypeInId;
6870

6971
try {
@@ -84,11 +86,38 @@ protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMa
8486
*/
8587
protected abstract void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> data, Object changed);
8688

89+
/**
90+
* Creates a Map for the id.
91+
*
92+
* <p>
93+
* The ordinal parameter represents the iteration ordinal of the current element, used to add a synthetic id when
94+
* dealing with embeddables since embeddable fields can't be contained within the primary key since they might be
95+
* nullable.
96+
* </p>
97+
*
98+
* @param ordinal
99+
* The element iteration ordinal.
100+
*
101+
* @return A Map for holding the ID information.
102+
*/
103+
protected Map<String, Object> createIdMap(int ordinal) {
104+
final HashMap<String, Object> idMap = new HashMap<String, Object>();
105+
106+
if ( ordinalInId ) {
107+
idMap.put( this.commonCollectionMapperData.getVerEntCfg().getEmbeddableSetOrdinalPropertyName(),
108+
Integer.valueOf( ordinal ) );
109+
}
110+
111+
return idMap;
112+
}
113+
87114
private void addCollectionChanges(SessionImplementor session, List<PersistentCollectionChangeData> collectionChanges,
88115
Set<Object> changed, RevisionType revisionType, Serializable id) {
116+
int ordinal = 0;
117+
89118
for (Object changedObj : changed) {
90119
Map<String, Object> entityData = new HashMap<String, Object>();
91-
Map<String, Object> originalId = new HashMap<String, Object>();
120+
Map<String, Object> originalId = createIdMap( ordinal++ );
92121
entityData.put(commonCollectionMapperData.getVerEntCfg().getOriginalIdPropName(), originalId);
93122

94123
collectionChanges.add(new PersistentCollectionChangeData(

hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/BasicCollectionMapper.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
public class BasicCollectionMapper<T extends Collection> extends AbstractCollectionMapper<T> implements PropertyMapper {
4242
protected final MiddleComponentData elementComponentData;
4343

44-
public BasicCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
45-
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
46-
MiddleComponentData elementComponentData, boolean revisionTypeInId) {
47-
super(commonCollectionMapperData, collectionClass, proxyClass, revisionTypeInId);
44+
public BasicCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
45+
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
46+
MiddleComponentData elementComponentData, boolean ordinalInId, boolean revisionTypeInId) {
47+
super( commonCollectionMapperData, collectionClass, proxyClass, ordinalInId, revisionTypeInId );
4848
this.elementComponentData = elementComponentData;
4949
}
5050

hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ListCollectionMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public final class ListCollectionMapper extends AbstractCollectionMapper<List> i
4848
public ListCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
4949
MiddleComponentData elementComponentData, MiddleComponentData indexComponentData,
5050
boolean revisionTypeInId) {
51-
super(commonCollectionMapperData, List.class, ListProxy.class, revisionTypeInId);
51+
super( commonCollectionMapperData, List.class, ListProxy.class, false, revisionTypeInId );
5252
this.elementComponentData = elementComponentData;
5353
this.indexComponentData = indexComponentData;
5454
}

hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/MapCollectionMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public MapCollectionMapper(CommonCollectionMapperData commonCollectionMapperData
4646
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
4747
MiddleComponentData elementComponentData, MiddleComponentData indexComponentData,
4848
boolean revisionTypeInId) {
49-
super(commonCollectionMapperData, collectionClass, proxyClass, revisionTypeInId);
49+
super( commonCollectionMapperData, collectionClass, proxyClass, false, revisionTypeInId );
5050
this.elementComponentData = elementComponentData;
5151
this.indexComponentData = indexComponentData;
5252
}

hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedSetCollectionMapper.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ public final class SortedSetCollectionMapper extends BasicCollectionMapper<Sorte
3838
private final Comparator comparator;
3939

4040
public SortedSetCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
41-
Class<? extends SortedSet> collectionClass, Class<? extends SortedSet> proxyClass,
42-
MiddleComponentData elementComponentData, Comparator comparator,
43-
boolean revisionTypeInId) {
44-
super(commonCollectionMapperData, collectionClass, proxyClass, elementComponentData, revisionTypeInId);
41+
Class<? extends SortedSet> collectionClass, Class<? extends SortedSet> proxyClass,
42+
MiddleComponentData elementComponentData, Comparator comparator, boolean ordinalInId,
43+
boolean revisionTypeInId) {
44+
super( commonCollectionMapperData, collectionClass, proxyClass, elementComponentData, ordinalInId,
45+
revisionTypeInId );
4546
this.comparator = comparator;
4647
}
4748

hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableSet.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public class EmbeddableSet extends BaseEnversJPAFunctionalTestCase {
5050
private final Component4 c4_2 = new Component4( "c42", "c42_value2", "c42_description" );
5151
private final Component3 c3_1 = new Component3( "c31", c4_1, c4_2 );
5252
private final Component3 c3_2 = new Component3( "c32", c4_1, c4_2 );
53+
private final Component3 c3_3 = new Component3( "c33", c4_1, c4_2 );
54+
private final Component3 c3_4 = new Component3( "c34", c4_1, c4_2 );
5355

5456
@Override
5557
protected Class<?>[] getAnnotatedClasses() {
@@ -63,9 +65,10 @@ public void initData() {
6365

6466
EmbeddableSetEntity ese1 = new EmbeddableSetEntity();
6567

66-
// Revision 1 (ese1: initially 1 element in both collections)
68+
// Revision 1 (ese1: initially 2 elements)
6769
em.getTransaction().begin();
6870
ese1.getComponentSet().add( c3_1 );
71+
ese1.getComponentSet().add( c3_3 );
6972
em.persist( ese1 );
7073
em.getTransaction().commit();
7174

@@ -93,24 +96,53 @@ public void initData() {
9396
ese1.getComponentSet().remove( c3_2 );
9497
em.getTransaction().commit();
9598

99+
// Revision 5 (ese1: adding two elements)
100+
em.getTransaction().begin();
101+
ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() );
102+
ese1.getComponentSet().add( c3_2 );
103+
ese1.getComponentSet().add( c3_4 );
104+
em.getTransaction().commit();
105+
106+
// Revision 6 (ese1: removing two elements)
107+
em.getTransaction().begin();
108+
ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() );
109+
ese1.getComponentSet().remove( c3_2 );
110+
ese1.getComponentSet().remove( c3_4 );
111+
em.getTransaction().commit();
112+
113+
// Revision 7 (ese1: removing and adding two elements)
114+
em.getTransaction().begin();
115+
ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() );
116+
ese1.getComponentSet().remove( c3_1 );
117+
ese1.getComponentSet().remove( c3_3 );
118+
ese1.getComponentSet().add( c3_2 );
119+
ese1.getComponentSet().add( c3_4 );
120+
em.getTransaction().commit();
121+
96122
ese1_id = ese1.getId();
97123

98124
em.close();
99125
}
100126

101127
@Test
102128
public void testRevisionsCounts() {
103-
assertEquals( Arrays.asList( 1, 2, 3 ), getAuditReader().getRevisions( EmbeddableSetEntity.class, ese1_id ) );
129+
assertEquals( Arrays.asList( 1, 2, 3, 4, 5, 6 ), getAuditReader().getRevisions( EmbeddableSetEntity.class, ese1_id ) );
104130
}
105131

106132
@Test
107133
public void testHistoryOfEse1() {
108134
EmbeddableSetEntity rev1 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 1 );
109135
EmbeddableSetEntity rev2 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 2 );
110136
EmbeddableSetEntity rev3 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 3 );
111-
112-
assertEquals( Collections.singleton( c3_1 ), rev1.getComponentSet() );
113-
assertEquals( TestTools.makeSet( c3_1, c3_2 ), rev2.getComponentSet() );
114-
assertEquals( TestTools.makeSet( c3_1 ), rev3.getComponentSet() );
137+
EmbeddableSetEntity rev4 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 4 );
138+
EmbeddableSetEntity rev5 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 5 );
139+
EmbeddableSetEntity rev6 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 6 );
140+
141+
assertEquals( TestTools.makeSet( c3_1, c3_3 ), rev1.getComponentSet() );
142+
assertEquals( TestTools.makeSet( c3_1, c3_2, c3_3 ), rev2.getComponentSet() );
143+
assertEquals( TestTools.makeSet( c3_1, c3_3 ), rev3.getComponentSet() );
144+
assertEquals( TestTools.makeSet( c3_1, c3_2, c3_3, c3_4 ), rev4.getComponentSet() );
145+
assertEquals( TestTools.makeSet( c3_1, c3_3 ), rev5.getComponentSet() );
146+
assertEquals( TestTools.makeSet( c3_2, c3_4 ), rev6.getComponentSet() );
115147
}
116148
}

0 commit comments

Comments
 (0)