From b5755b6945d8c5e528da890097de5cf2b93c15c3 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sat, 18 Dec 2021 17:37:18 -0500 Subject: [PATCH] HHH-13361 Allow auditing entities with nested identifiers --- .../metadata/IdMetadataGenerator.java | 25 +++- .../mapper/id/AbstractCompositeIdMapper.java | 23 +++- .../entities/mapper/id/AbstractIdMapper.java | 4 + .../entities/mapper/id/EmbeddedIdMapper.java | 12 +- .../internal/entities/mapper/id/IdMapper.java | 2 + .../entities/mapper/id/MultipleIdMapper.java | 17 ++- .../mapper/id/NestedEmbeddedIdMapper.java | 22 ++++ .../mapper/id/SimpleIdMapperBuilder.java | 11 ++ .../entities/mapper/id/SingleIdMapper.java | 9 ++ .../ids/embeddedid/CompositeEntity.java | 71 ++++++++++++ .../ids/embeddedid/CompositeEntityId.java | 42 +++++++ .../NestedEmbeddedIdentifiersTest.java | 107 ++++++++++++++++++ .../ids/embeddedid/OwnerOfRelationCode.java | 86 ++++++++++++++ .../ids/embeddedid/OwnerOfRelationCodeId.java | 45 ++++++++ 14 files changed, 460 insertions(+), 16 deletions(-) create mode 100644 hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/NestedEmbeddedIdMapper.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntityId.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/NestedEmbeddedIdentifiersTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCode.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCodeId.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/IdMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/IdMetadataGenerator.java index 05b2e0ef59..7c65f06b61 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/IdMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/IdMetadataGenerator.java @@ -24,6 +24,7 @@ import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder; import org.hibernate.envers.internal.entities.mapper.id.EmbeddedIdMapper; import org.hibernate.envers.internal.entities.mapper.id.IdMapper; import org.hibernate.envers.internal.entities.mapper.id.MultipleIdMapper; +import org.hibernate.envers.internal.entities.mapper.id.NestedEmbeddedIdMapper; import org.hibernate.envers.internal.entities.mapper.id.SimpleIdMapperBuilder; import org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper; import org.hibernate.loader.PropertyPath; @@ -32,6 +33,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; +import org.hibernate.type.ComponentType; import org.hibernate.type.ManyToOneType; import org.hibernate.type.Type; @@ -63,7 +65,8 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator { boolean key, SimpleIdMapperBuilder mapper, Property mappedProperty, - Property virtualProperty) { + Property virtualProperty, + boolean audited) { if ( PropertyPath.IDENTIFIER_MAPPER_PROPERTY.equals( mappedProperty.getName() ) ) { return false; @@ -93,6 +96,19 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator { return added; } + else if ( ComponentType.class.isInstance( mappedProperty.getType() ) ) { + final Component component = (Component) mappedProperty.getValue(); + final NestedEmbeddedIdMapper nestedMapper; + if ( mapper != null ) { + final PropertyData propertyData = propertyAuditingData.resolvePropertyData(); + nestedMapper = new NestedEmbeddedIdMapper( propertyData, component ); + mapper.add( propertyData, nestedMapper ); + } + else { + nestedMapper = null; + } + return addIdProperties( attributeContainer, component, null, nestedMapper, key, audited ); + } return addBasic( attributeContainer, propertyAuditingData, mappedProperty.getValue(), mapper, key ); } @@ -116,7 +132,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator { virtualProperty = null; } - if ( !addIdProperty( attributeContainer, key, mapper, property, virtualProperty ) ) { + if ( !addIdProperty( attributeContainer, key, mapper, property, virtualProperty, audited ) ) { // If the entity is audited, and a non-supported id component is used, throw exception. if ( audited ) { throw new EnversMappingException( @@ -196,7 +212,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator { if ( idMapper != null ) { // Multiple id final Component virtualComponent = (Component) persistentClass.getIdentifier(); - mapper = new MultipleIdMapper( loadClass( virtualComponent ), persistentClass.getServiceRegistry() ); + mapper = new MultipleIdMapper( virtualComponent ); if ( !addIdProperties( relation, idMapper, virtualComponent, mapper, false, audited ) ) { return null; @@ -210,8 +226,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator { else if ( idProp.isComposite() ) { // Embedded id final Component idComponent = (Component) idProp.getValue(); - final Class embeddableClass = loadClass( idComponent ); - mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), embeddableClass, persistentClass.getServiceRegistry() ); + mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), idComponent ); if ( !addIdProperties( relation, idComponent, null, mapper, false, audited ) ) { return null; diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractCompositeIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractCompositeIdMapper.java index 0a99b08815..cee7177971 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractCompositeIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractCompositeIdMapper.java @@ -17,15 +17,18 @@ import org.hibernate.internal.util.ReflectHelper; import org.hibernate.service.ServiceRegistry; /** + * An abstract identifier mapper implementation specific for composite identifiers. + * * @author Adam Warski (adam at warski dot org) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + * @author Chris Cranford */ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder { - protected final Class compositeIdClass; + protected final Class compositeIdClass; - protected Map ids; + protected Map ids; - protected AbstractCompositeIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) { + protected AbstractCompositeIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) { super( serviceRegistry ); this.compositeIdClass = compositeIdClass; ids = Tools.newLinkedHashMap(); @@ -33,7 +36,12 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme @Override public void add(PropertyData propertyData) { - ids.put( propertyData, new SingleIdMapper( getServiceRegistry(), propertyData ) ); + add( propertyData, new SingleIdMapper( getServiceRegistry(), propertyData ) ); + } + + @Override + public void add(PropertyData propertyData, AbstractIdMapper idMapper) { + ids.put( propertyData, idMapper ); } @Override @@ -43,7 +51,7 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme } final Object compositeId = instantiateCompositeId(); - for ( SingleIdMapper mapper : ids.values() ) { + for ( AbstractIdMapper mapper : ids.values() ) { if ( !mapper.mapToEntityFromMap( compositeId, data ) ) { return null; } @@ -52,6 +60,11 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme return compositeId; } + @Override + public void mapToEntityFromEntity(Object objectTo, Object objectFrom) { + // no-op; does nothing + } + protected Object instantiateCompositeId() { return AccessController.doPrivileged( new PrivilegedAction() { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractIdMapper.java index 07ede371fb..3c1801da2d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/AbstractIdMapper.java @@ -13,6 +13,8 @@ import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.service.ServiceRegistry; /** + * The base abstract class implementation for identifier mappers. + * * @author Adam Warski (adam at warski dot org) * @author Chris Cranford */ @@ -139,6 +141,8 @@ public abstract class AbstractIdMapper implements IdMapper { } } + public abstract void mapToEntityFromEntity(Object objectTo, Object objectFrom); + private void handleNullValue(Parameters parameters, String alias, String propertyName, boolean equals) { if ( equals ) { parameters.addNullRestriction( alias, propertyName ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java index 059345fbdd..ad9673540d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/EmbeddedIdMapper.java @@ -16,19 +16,27 @@ import java.util.Map; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.tools.ReflectionTools; +import org.hibernate.mapping.Component; import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Setter; import org.hibernate.service.ServiceRegistry; /** + * An identifier mapper implementation for {@link jakarta.persistence.EmbeddedId} mappings. + * * @author Adam Warski (adam at warski dot org) + * @author Chris Cranford */ public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder { private PropertyData idPropertyData; - public EmbeddedIdMapper(PropertyData idPropertyData, Class compositeIdClass, ServiceRegistry serviceRegistry) { - super( compositeIdClass, serviceRegistry ); + public EmbeddedIdMapper(PropertyData propertyData, Component component) { + super( component.getComponentClass(), component.getServiceRegistry() ); + this.idPropertyData = propertyData; + } + private EmbeddedIdMapper(PropertyData idPropertyData, Class compositeIdClass, ServiceRegistry serviceRegistry) { + super( compositeIdClass, serviceRegistry ); this.idPropertyData = idPropertyData; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java index d447a3f7ae..dece33eeb9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/IdMapper.java @@ -14,6 +14,8 @@ import org.hibernate.envers.internal.tools.query.Parameters; import org.hibernate.service.ServiceRegistry; /** + * Base contract for all identifier mappers. + * * @author Adam Warski (adam at warski dot org) * @author Chris Cranford */ diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java index f7bea641e5..edf11ae9f6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/MultipleIdMapper.java @@ -13,14 +13,23 @@ import java.util.Map; import org.hibernate.Session; import org.hibernate.envers.internal.entities.PropertyData; +import org.hibernate.mapping.Component; import org.hibernate.service.ServiceRegistry; /** + * An implementation of an identifier mapper for {@link jakarta.persistence.IdClass} or multiple + * {@link jakarta.persistence.Id} identifier mappings. + * * @author Adam Warski (adam at warski dot org) * @author Chris Cranford */ public class MultipleIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder { - public MultipleIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) { + + public MultipleIdMapper(Component component) { + super( component.getComponentClass(), component.getServiceRegistry() ); + } + + private MultipleIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) { super( compositeIdClass, serviceRegistry ); } @@ -32,9 +41,9 @@ public class MultipleIdMapper extends AbstractCompositeIdMapper implements Simpl @Override public void mapToMapFromId(Session session, Map data, Object obj) { if ( compositeIdClass.isInstance( obj ) ) { - for ( Map.Entry entry : ids.entrySet() ) { + for ( Map.Entry entry : ids.entrySet() ) { final PropertyData propertyData = entry.getKey(); - final SingleIdMapper idMapper = entry.getValue(); + final AbstractIdMapper idMapper = entry.getValue(); if ( propertyData.getVirtualReturnClass() == null ) { idMapper.mapToMapFromEntity( data, obj ); @@ -92,7 +101,7 @@ public class MultipleIdMapper extends AbstractCompositeIdMapper implements Simpl } final Object compositeId = instantiateCompositeId(); - for ( SingleIdMapper mapper : ids.values() ) { + for ( AbstractIdMapper mapper : ids.values() ) { mapper.mapToEntityFromEntity( compositeId, data ); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/NestedEmbeddedIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/NestedEmbeddedIdMapper.java new file mode 100644 index 0000000000..2cb3ab6fe2 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/NestedEmbeddedIdMapper.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.internal.entities.mapper.id; + +import org.hibernate.envers.internal.entities.PropertyData; +import org.hibernate.mapping.Component; + +/** + * An identifier mapper that is meant to support nested {@link jakarta.persistence.Embeddable} instances + * inside an existing {@link jakarta.persistence.EmbeddedId} identifier hierarchy. + * + * @author Chris Cranford + */ +public class NestedEmbeddedIdMapper extends EmbeddedIdMapper { + public NestedEmbeddedIdMapper(PropertyData propertyData, Component component) { + super( propertyData, component ); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SimpleIdMapperBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SimpleIdMapperBuilder.java index 4d0b6105c0..eb9061676b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SimpleIdMapperBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SimpleIdMapperBuilder.java @@ -6,10 +6,21 @@ */ package org.hibernate.envers.internal.entities.mapper.id; +import org.hibernate.envers.internal.entities.PropertyData; import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder; /** + * A simple identifier builder contract. + * * @author Adam Warski (adam at warski dot org) + * @author Chris Cranford */ public interface SimpleIdMapperBuilder extends IdMapper, SimpleMapperBuilder { + /** + * Add a custom identifier mapper to the builder. + * + * @param propertyData the property data + * @param idMapper the mapper + */ + void add(PropertyData propertyData, AbstractIdMapper idMapper); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SingleIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SingleIdMapper.java index e104174b61..20b4ce329f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SingleIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/id/SingleIdMapper.java @@ -21,7 +21,10 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.service.ServiceRegistry; /** + * An implementation of an identifier mapper for a single basic attribute property. + * * @author Adam Warski (adam at warski dot org) + * @author Chris Cranford */ public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder { private PropertyData propertyData; @@ -44,6 +47,11 @@ public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBu this.propertyData = propertyData; } + @Override + public void add(PropertyData propertyData, AbstractIdMapper idMapper) { + throw new AuditException( "This method is not allowed for a single identifier mapper" ); + } + @Override public boolean mapToEntityFromMap(final Object obj, Map data) { if ( data == null || obj == null ) { @@ -143,6 +151,7 @@ public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBu } } + @Override public void mapToEntityFromEntity(final Object objTo, final Object objFrom) { if ( objTo == null || objFrom == null ) { return; diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntity.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntity.java new file mode 100644 index 0000000000..609a4f37e8 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntity.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.envers.integration.ids.embeddedid; + +import java.util.Set; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import org.hibernate.envers.Audited; + +@Entity +@Audited +@Table(name = "compositeentity") +@Access(value = AccessType.FIELD) +public class CompositeEntity { + + @EmbeddedId + private CompositeEntityId codeObject = new CompositeEntityId(); + + @OneToMany(targetEntity = OwnerOfRelationCode.class, fetch = FetchType.LAZY, mappedBy = "compositeEntity") + private Set ownerOfRelationCodes = new java.util.HashSet<>(); + + public CompositeEntityId getCodeObject() { + return codeObject; + } + + public void setCodeObject(CompositeEntityId codeObject) { + this.codeObject = codeObject; + } + + public Set getOwnerOfRelationCodes() { + return ownerOfRelationCodes; + } + + public void setOwnerOfRelationCodes(Set ownerOfRelationCodes) { + this.ownerOfRelationCodes = ownerOfRelationCodes; + } + + public String getFirstCode() { + return codeObject == null ? null : codeObject.getFirstCode(); + } + + public void setFirstCode(String firstCode) { + if ( codeObject == null ) { + codeObject = new CompositeEntityId(); + } + codeObject.setFirstCode( firstCode ); + } + + public String getSecondCode() { + return codeObject == null ? null : codeObject.getSecondCode(); + } + + public void setSecondCode(String secondCode) { + if ( codeObject == null ) { + codeObject = new CompositeEntityId(); + } + codeObject.setSecondCode( secondCode ); + } + +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntityId.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntityId.java new file mode 100644 index 0000000000..6c8025d418 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/CompositeEntityId.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.envers.integration.ids.embeddedid; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class CompositeEntityId implements Serializable{ + + private String firstCode; + private String secondCode; + + public String getFirstCode() { + return firstCode; + } + + public void setFirstCode(String firstCode) { + this.firstCode = firstCode; + } + + public String getSecondCode() { + return secondCode; + } + + public void setSecondCode(String secondCode) { + this.secondCode = secondCode; + } + + @Override + public String toString() { + return "CompositeEntityId{" + + "firstCode='" + firstCode + '\'' + + ", secondCode='" + secondCode + '\'' + + '}'; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/NestedEmbeddedIdentifiersTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/NestedEmbeddedIdentifiersTest.java new file mode 100644 index 0000000000..3ee51d140b --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/NestedEmbeddedIdentifiersTest.java @@ -0,0 +1,107 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.envers.integration.ids.embeddedid; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; + +import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +@TestForIssue(jiraKey = "HHH-13361") +public class NestedEmbeddedIdentifiersTest extends BaseEnversJPAFunctionalTestCase { + + private OwnerOfRelationCodeId id; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { OwnerOfRelationCode.class, CompositeEntity.class }; + } + + @Test + @Priority(10) + public void initData() { + // Revision 1, test insert + final OwnerOfRelationCode owner = doInJPA( this::entityManagerFactory, session -> { + CompositeEntity compositeEntity = new CompositeEntity(); + compositeEntity.setFirstCode( "firstCode" ); + compositeEntity.setSecondCode( "secondCode" ); + session.persist( compositeEntity ); + + OwnerOfRelationCode ownerEntity = new OwnerOfRelationCode(); + ownerEntity.setCompositeEntity( compositeEntity ); + ownerEntity.setSecondIdentifier( "secondIdentifier" ); + + session.persist( ownerEntity ); + return ownerEntity; + } ); + + this.id = owner.getCodeObject(); + + // Revision 2, test update + doInJPA( this::entityManagerFactory, session -> { + OwnerOfRelationCode ownerEntity = session.find( OwnerOfRelationCode.class, id ); + ownerEntity.setDescription( "first description" ); + } ); + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2 ), getAuditReader().getRevisions( OwnerOfRelationCode.class, id ) ); + } + + @Test + public void testIdentifierAtRevision1() { + // select e__ + // from org.hibernate.orm.test.envers.integration.ids.embeddedid.OwnerOfRelationCode_AUD e__ + // where e__.originalId.REV.id = (select max(e2__.originalId.REV.id) + // from org.hibernate.orm.test.envers.integration.ids.embeddedid.OwnerOfRelationCode_AUD e2__ + // where e2__.originalId.REV.id <= :revision + // and e__.originalId.compositeEntity = e2__.originalId.compositeEntity + // and e__.originalId.secondIdentifier = e2__.originalId.secondIdentifier) + // and e__.REVTYPE <> :_p0 + // and e__.originalId.compositeEntity_firstCode = :_p1 + // and e__.originalId.compositeEntity_secondCode = :_p2 + // and e__.originalId.secondIdentifier = :_p3 + // + // select e__ + // from org.hibernate.orm.test.envers.integration.ids.embeddedid.OwnerOfRelationCode_AUD e__ + // where e__.originalId.REV.id = (select max(e2__.originalId.REV.id) + // from org.hibernate.orm.test.envers.integration.ids.embeddedid.OwnerOfRelationCode_AUD e2__ + // where e2__.originalId.REV.id <= :revision + // and e__.originalId.compositeEntity_firstCode = e2__.originalId.compositeEntity_firstCode + // and e__.originalId.compositeEntity_secondCode = e2__.originalId.compositeEntity_secondCode + // and e__.originalId.secondIdentifier = e2__.originalId.secondIdentifier) + // and e__.REVTYPE <> :_p0 + // and e__.originalId.compositeEntity_firstCode = :_p1 + // and e__.originalId.compositeEntity_secondCode = :_p2 + // and e__.originalId.secondIdentifier = :_p3 + final OwnerOfRelationCode rev1 = getAuditReader().find( OwnerOfRelationCode.class, id, 1 ); + System.out.println( rev1 ); + assertEquals( rev1.getCodeObject().getSecondIdentifier(), "secondIdentifier" ); + assertEquals( rev1.getCodeObject().getCompositeEntity().getFirstCode(), "firstCode" ); + assertEquals( rev1.getCodeObject().getCompositeEntity().getSecondCode(), "secondCode" ); + assertNull( rev1.getDescription() ); + } + + @Test + public void testIdentifierAtRevision2() { + final OwnerOfRelationCode rev2 = getAuditReader().find( OwnerOfRelationCode.class, id, 2 ); + System.out.println( rev2 ); + assertEquals( rev2.getCodeObject().getSecondIdentifier(), "secondIdentifier" ); + assertEquals( rev2.getCodeObject().getCompositeEntity().getFirstCode(), "firstCode" ); + assertEquals( rev2.getCodeObject().getCompositeEntity().getSecondCode(), "secondCode" ); + assertEquals( rev2.getDescription(), "first description" ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCode.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCode.java new file mode 100644 index 0000000000..bb33f35bca --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCode.java @@ -0,0 +1,86 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.envers.integration.ids.embeddedid; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +import org.hibernate.envers.Audited; + +@Entity +@Audited +@Table(name = "ownerOfRelationcode") +@Access(value = AccessType.FIELD) +public class OwnerOfRelationCode { + + @EmbeddedId + private OwnerOfRelationCodeId codeObject = new OwnerOfRelationCodeId(); + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumns({ + @JoinColumn(name = "compositeEntity_firstCode", referencedColumnName = "firstCode", unique = false, nullable = false), + @JoinColumn(name = "compositeEntity_secondCode", referencedColumnName = "secondCode", unique = false, nullable = false) }) + @MapsId("compositeEntity") + private CompositeEntity compositeEntity; + + private String description; + + public OwnerOfRelationCodeId getCodeObject() { + return codeObject; + } + + public CompositeEntity getCompositeEntity() { + return compositeEntity; + } + + public void setCompositeEntity(CompositeEntity compositeEntity) { + if ( codeObject == null ) { + codeObject = new OwnerOfRelationCodeId(); + } + if ( compositeEntity != null ) { + codeObject.setCompositeEntity( compositeEntity.getCodeObject() ); + } + this.compositeEntity = compositeEntity; + } + + public String getSecondIdentifier() { + return codeObject == null ? null : codeObject.getSecondIdentifier(); + + } + + public void setSecondIdentifier(String secondIdentifier) { + if ( codeObject == null ) { + codeObject = new OwnerOfRelationCodeId(); + } + codeObject.setSecondIdentifier( secondIdentifier ); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return "OwnerOfRelationCode{" + + "codeObject=" + codeObject + + ", compositeEntity=" + compositeEntity + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCodeId.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCodeId.java new file mode 100644 index 0000000000..910c066e84 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/ids/embeddedid/OwnerOfRelationCodeId.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.envers.integration.ids.embeddedid; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; + +@Embeddable +public class OwnerOfRelationCodeId implements Serializable { + + @Embedded + private CompositeEntityId compositeEntity; + + private String secondIdentifier; + + public CompositeEntityId getCompositeEntity() { + return compositeEntity; + } + + public void setCompositeEntity(CompositeEntityId compositeEntity) { + this.compositeEntity = compositeEntity; + } + + public String getSecondIdentifier() { + return secondIdentifier; + } + + public void setSecondIdentifier(String secondIdentifier) { + this.secondIdentifier = secondIdentifier; + } + + @Override + public String toString() { + return "OwnerOfRelationCodeId{" + + "compositeEntity=" + compositeEntity + + ", secondIdentifier='" + secondIdentifier + '\'' + + '}'; + } +}