Skip to content

Commit b5755b6

Browse files
committed
HHH-13361 Allow auditing entities with nested identifiers
1 parent c5f719e commit b5755b6

File tree

14 files changed

+460
-16
lines changed

14 files changed

+460
-16
lines changed

hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/IdMetadataGenerator.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.hibernate.envers.internal.entities.mapper.id.EmbeddedIdMapper;
2525
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
2626
import org.hibernate.envers.internal.entities.mapper.id.MultipleIdMapper;
27+
import org.hibernate.envers.internal.entities.mapper.id.NestedEmbeddedIdMapper;
2728
import org.hibernate.envers.internal.entities.mapper.id.SimpleIdMapperBuilder;
2829
import org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper;
2930
import org.hibernate.loader.PropertyPath;
@@ -32,6 +33,7 @@
3233
import org.hibernate.mapping.Property;
3334
import org.hibernate.mapping.ToOne;
3435
import org.hibernate.mapping.Value;
36+
import org.hibernate.type.ComponentType;
3537
import org.hibernate.type.ManyToOneType;
3638
import org.hibernate.type.Type;
3739

@@ -63,7 +65,8 @@ private boolean addIdProperty(
6365
boolean key,
6466
SimpleIdMapperBuilder mapper,
6567
Property mappedProperty,
66-
Property virtualProperty) {
68+
Property virtualProperty,
69+
boolean audited) {
6770

6871
if ( PropertyPath.IDENTIFIER_MAPPER_PROPERTY.equals( mappedProperty.getName() ) ) {
6972
return false;
@@ -93,6 +96,19 @@ private boolean addIdProperty(
9396

9497
return added;
9598
}
99+
else if ( ComponentType.class.isInstance( mappedProperty.getType() ) ) {
100+
final Component component = (Component) mappedProperty.getValue();
101+
final NestedEmbeddedIdMapper nestedMapper;
102+
if ( mapper != null ) {
103+
final PropertyData propertyData = propertyAuditingData.resolvePropertyData();
104+
nestedMapper = new NestedEmbeddedIdMapper( propertyData, component );
105+
mapper.add( propertyData, nestedMapper );
106+
}
107+
else {
108+
nestedMapper = null;
109+
}
110+
return addIdProperties( attributeContainer, component, null, nestedMapper, key, audited );
111+
}
96112

97113
return addBasic( attributeContainer, propertyAuditingData, mappedProperty.getValue(), mapper, key );
98114
}
@@ -116,7 +132,7 @@ private boolean addIdProperties(
116132
virtualProperty = null;
117133
}
118134

119-
if ( !addIdProperty( attributeContainer, key, mapper, property, virtualProperty ) ) {
135+
if ( !addIdProperty( attributeContainer, key, mapper, property, virtualProperty, audited ) ) {
120136
// If the entity is audited, and a non-supported id component is used, throw exception.
121137
if ( audited ) {
122138
throw new EnversMappingException(
@@ -196,7 +212,7 @@ public IdMappingData addIdAndGetMappingData(PersistentClass persistentClass, boo
196212
if ( idMapper != null ) {
197213
// Multiple id
198214
final Component virtualComponent = (Component) persistentClass.getIdentifier();
199-
mapper = new MultipleIdMapper( loadClass( virtualComponent ), persistentClass.getServiceRegistry() );
215+
mapper = new MultipleIdMapper( virtualComponent );
200216

201217
if ( !addIdProperties( relation, idMapper, virtualComponent, mapper, false, audited ) ) {
202218
return null;
@@ -210,8 +226,7 @@ public IdMappingData addIdAndGetMappingData(PersistentClass persistentClass, boo
210226
else if ( idProp.isComposite() ) {
211227
// Embedded id
212228
final Component idComponent = (Component) idProp.getValue();
213-
final Class<?> embeddableClass = loadClass( idComponent );
214-
mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), embeddableClass, persistentClass.getServiceRegistry() );
229+
mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), idComponent );
215230

216231
if ( !addIdProperties( relation, idComponent, null, mapper, false, audited ) ) {
217232
return null;

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractCompositeIdMapper.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,31 @@
1717
import org.hibernate.service.ServiceRegistry;
1818

1919
/**
20+
* An abstract identifier mapper implementation specific for composite identifiers.
21+
*
2022
* @author Adam Warski (adam at warski dot org)
2123
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
24+
* @author Chris Cranford
2225
*/
2326
public abstract class AbstractCompositeIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
24-
protected final Class compositeIdClass;
27+
protected final Class<?> compositeIdClass;
2528

26-
protected Map<PropertyData, SingleIdMapper> ids;
29+
protected Map<PropertyData, AbstractIdMapper> ids;
2730

28-
protected AbstractCompositeIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) {
31+
protected AbstractCompositeIdMapper(Class<?> compositeIdClass, ServiceRegistry serviceRegistry) {
2932
super( serviceRegistry );
3033
this.compositeIdClass = compositeIdClass;
3134
ids = Tools.newLinkedHashMap();
3235
}
3336

3437
@Override
3538
public void add(PropertyData propertyData) {
36-
ids.put( propertyData, new SingleIdMapper( getServiceRegistry(), propertyData ) );
39+
add( propertyData, new SingleIdMapper( getServiceRegistry(), propertyData ) );
40+
}
41+
42+
@Override
43+
public void add(PropertyData propertyData, AbstractIdMapper idMapper) {
44+
ids.put( propertyData, idMapper );
3745
}
3846

3947
@Override
@@ -43,7 +51,7 @@ public Object mapToIdFromMap(Map data) {
4351
}
4452

4553
final Object compositeId = instantiateCompositeId();
46-
for ( SingleIdMapper mapper : ids.values() ) {
54+
for ( AbstractIdMapper mapper : ids.values() ) {
4755
if ( !mapper.mapToEntityFromMap( compositeId, data ) ) {
4856
return null;
4957
}
@@ -52,6 +60,11 @@ public Object mapToIdFromMap(Map data) {
5260
return compositeId;
5361
}
5462

63+
@Override
64+
public void mapToEntityFromEntity(Object objectTo, Object objectFrom) {
65+
// no-op; does nothing
66+
}
67+
5568
protected Object instantiateCompositeId() {
5669
return AccessController.doPrivileged(
5770
new PrivilegedAction<Object>() {

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractIdMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.hibernate.service.ServiceRegistry;
1414

1515
/**
16+
* The base abstract class implementation for identifier mappers.
17+
*
1618
* @author Adam Warski (adam at warski dot org)
1719
* @author Chris Cranford
1820
*/
@@ -139,6 +141,8 @@ public void addNamedIdEqualsToQuery(Parameters parameters, String prefix1, IdMap
139141
}
140142
}
141143

144+
public abstract void mapToEntityFromEntity(Object objectTo, Object objectFrom);
145+
142146
private void handleNullValue(Parameters parameters, String alias, String propertyName, boolean equals) {
143147
if ( equals ) {
144148
parameters.addNullRestriction( alias, propertyName );

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,27 @@
1616
import org.hibernate.envers.exception.AuditException;
1717
import org.hibernate.envers.internal.entities.PropertyData;
1818
import org.hibernate.envers.internal.tools.ReflectionTools;
19+
import org.hibernate.mapping.Component;
1920
import org.hibernate.property.access.spi.Getter;
2021
import org.hibernate.property.access.spi.Setter;
2122
import org.hibernate.service.ServiceRegistry;
2223

2324
/**
25+
* An identifier mapper implementation for {@link jakarta.persistence.EmbeddedId} mappings.
26+
*
2427
* @author Adam Warski (adam at warski dot org)
28+
* @author Chris Cranford
2529
*/
2630
public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
2731
private PropertyData idPropertyData;
2832

29-
public EmbeddedIdMapper(PropertyData idPropertyData, Class compositeIdClass, ServiceRegistry serviceRegistry) {
30-
super( compositeIdClass, serviceRegistry );
33+
public EmbeddedIdMapper(PropertyData propertyData, Component component) {
34+
super( component.getComponentClass(), component.getServiceRegistry() );
35+
this.idPropertyData = propertyData;
36+
}
3137

38+
private EmbeddedIdMapper(PropertyData idPropertyData, Class<?> compositeIdClass, ServiceRegistry serviceRegistry) {
39+
super( compositeIdClass, serviceRegistry );
3240
this.idPropertyData = idPropertyData;
3341
}
3442

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import org.hibernate.service.ServiceRegistry;
1515

1616
/**
17+
* Base contract for all identifier mappers.
18+
*
1719
* @author Adam Warski (adam at warski dot org)
1820
* @author Chris Cranford
1921
*/

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@
1313

1414
import org.hibernate.Session;
1515
import org.hibernate.envers.internal.entities.PropertyData;
16+
import org.hibernate.mapping.Component;
1617
import org.hibernate.service.ServiceRegistry;
1718

1819
/**
20+
* An implementation of an identifier mapper for {@link jakarta.persistence.IdClass} or multiple
21+
* {@link jakarta.persistence.Id} identifier mappings.
22+
*
1923
* @author Adam Warski (adam at warski dot org)
2024
* @author Chris Cranford
2125
*/
2226
public class MultipleIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
23-
public MultipleIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) {
27+
28+
public MultipleIdMapper(Component component) {
29+
super( component.getComponentClass(), component.getServiceRegistry() );
30+
}
31+
32+
private MultipleIdMapper(Class<?> compositeIdClass, ServiceRegistry serviceRegistry) {
2433
super( compositeIdClass, serviceRegistry );
2534
}
2635

@@ -32,9 +41,9 @@ public void add(PropertyData propertyData) {
3241
@Override
3342
public void mapToMapFromId(Session session, Map<String, Object> data, Object obj) {
3443
if ( compositeIdClass.isInstance( obj ) ) {
35-
for ( Map.Entry<PropertyData, SingleIdMapper> entry : ids.entrySet() ) {
44+
for ( Map.Entry<PropertyData, AbstractIdMapper> entry : ids.entrySet() ) {
3645
final PropertyData propertyData = entry.getKey();
37-
final SingleIdMapper idMapper = entry.getValue();
46+
final AbstractIdMapper idMapper = entry.getValue();
3847

3948
if ( propertyData.getVirtualReturnClass() == null ) {
4049
idMapper.mapToMapFromEntity( data, obj );
@@ -92,7 +101,7 @@ public Object mapToIdFromEntity(Object data) {
92101
}
93102

94103
final Object compositeId = instantiateCompositeId();
95-
for ( SingleIdMapper mapper : ids.values() ) {
104+
for ( AbstractIdMapper mapper : ids.values() ) {
96105
mapper.mapToEntityFromEntity( compositeId, data );
97106
}
98107

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.envers.internal.entities.mapper.id;
8+
9+
import org.hibernate.envers.internal.entities.PropertyData;
10+
import org.hibernate.mapping.Component;
11+
12+
/**
13+
* An identifier mapper that is meant to support nested {@link jakarta.persistence.Embeddable} instances
14+
* inside an existing {@link jakarta.persistence.EmbeddedId} identifier hierarchy.
15+
*
16+
* @author Chris Cranford
17+
*/
18+
public class NestedEmbeddedIdMapper extends EmbeddedIdMapper {
19+
public NestedEmbeddedIdMapper(PropertyData propertyData, Component component) {
20+
super( propertyData, component );
21+
}
22+
}

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SimpleIdMapperBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@
66
*/
77
package org.hibernate.envers.internal.entities.mapper.id;
88

9+
import org.hibernate.envers.internal.entities.PropertyData;
910
import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
1011

1112
/**
13+
* A simple identifier builder contract.
14+
*
1215
* @author Adam Warski (adam at warski dot org)
16+
* @author Chris Cranford
1317
*/
1418
public interface SimpleIdMapperBuilder extends IdMapper, SimpleMapperBuilder {
19+
/**
20+
* Add a custom identifier mapper to the builder.
21+
*
22+
* @param propertyData the property data
23+
* @param idMapper the mapper
24+
*/
25+
void add(PropertyData propertyData, AbstractIdMapper idMapper);
1526
}

hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SingleIdMapper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
import org.hibernate.service.ServiceRegistry;
2222

2323
/**
24+
* An implementation of an identifier mapper for a single basic attribute property.
25+
*
2426
* @author Adam Warski (adam at warski dot org)
27+
* @author Chris Cranford
2528
*/
2629
public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
2730
private PropertyData propertyData;
@@ -44,6 +47,11 @@ public void add(PropertyData propertyData) {
4447
this.propertyData = propertyData;
4548
}
4649

50+
@Override
51+
public void add(PropertyData propertyData, AbstractIdMapper idMapper) {
52+
throw new AuditException( "This method is not allowed for a single identifier mapper" );
53+
}
54+
4755
@Override
4856
public boolean mapToEntityFromMap(final Object obj, Map data) {
4957
if ( data == null || obj == null ) {
@@ -143,6 +151,7 @@ public Object run() {
143151
}
144152
}
145153

154+
@Override
146155
public void mapToEntityFromEntity(final Object objTo, final Object objFrom) {
147156
if ( objTo == null || objFrom == null ) {
148157
return;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.envers.integration.ids.embeddedid;
8+
9+
import java.util.Set;
10+
11+
import jakarta.persistence.Access;
12+
import jakarta.persistence.AccessType;
13+
import jakarta.persistence.EmbeddedId;
14+
import jakarta.persistence.Entity;
15+
import jakarta.persistence.FetchType;
16+
import jakarta.persistence.OneToMany;
17+
import jakarta.persistence.Table;
18+
19+
import org.hibernate.envers.Audited;
20+
21+
@Entity
22+
@Audited
23+
@Table(name = "compositeentity")
24+
@Access(value = AccessType.FIELD)
25+
public class CompositeEntity {
26+
27+
@EmbeddedId
28+
private CompositeEntityId codeObject = new CompositeEntityId();
29+
30+
@OneToMany(targetEntity = OwnerOfRelationCode.class, fetch = FetchType.LAZY, mappedBy = "compositeEntity")
31+
private Set<OwnerOfRelationCode> ownerOfRelationCodes = new java.util.HashSet<>();
32+
33+
public CompositeEntityId getCodeObject() {
34+
return codeObject;
35+
}
36+
37+
public void setCodeObject(CompositeEntityId codeObject) {
38+
this.codeObject = codeObject;
39+
}
40+
41+
public Set<OwnerOfRelationCode> getOwnerOfRelationCodes() {
42+
return ownerOfRelationCodes;
43+
}
44+
45+
public void setOwnerOfRelationCodes(Set<OwnerOfRelationCode> ownerOfRelationCodes) {
46+
this.ownerOfRelationCodes = ownerOfRelationCodes;
47+
}
48+
49+
public String getFirstCode() {
50+
return codeObject == null ? null : codeObject.getFirstCode();
51+
}
52+
53+
public void setFirstCode(String firstCode) {
54+
if ( codeObject == null ) {
55+
codeObject = new CompositeEntityId();
56+
}
57+
codeObject.setFirstCode( firstCode );
58+
}
59+
60+
public String getSecondCode() {
61+
return codeObject == null ? null : codeObject.getSecondCode();
62+
}
63+
64+
public void setSecondCode(String secondCode) {
65+
if ( codeObject == null ) {
66+
codeObject = new CompositeEntityId();
67+
}
68+
codeObject.setSecondCode( secondCode );
69+
}
70+
71+
}

0 commit comments

Comments
 (0)