HHH-13361 Allow auditing entities with nested identifiers
This commit is contained in:
parent
c5f719ef39
commit
b5755b6945
|
@ -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.EmbeddedIdMapper;
|
||||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
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.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.SimpleIdMapperBuilder;
|
||||||
import org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper;
|
import org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper;
|
||||||
import org.hibernate.loader.PropertyPath;
|
import org.hibernate.loader.PropertyPath;
|
||||||
|
@ -32,6 +33,7 @@ import org.hibernate.mapping.PersistentClass;
|
||||||
import org.hibernate.mapping.Property;
|
import org.hibernate.mapping.Property;
|
||||||
import org.hibernate.mapping.ToOne;
|
import org.hibernate.mapping.ToOne;
|
||||||
import org.hibernate.mapping.Value;
|
import org.hibernate.mapping.Value;
|
||||||
|
import org.hibernate.type.ComponentType;
|
||||||
import org.hibernate.type.ManyToOneType;
|
import org.hibernate.type.ManyToOneType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -63,7 +65,8 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator {
|
||||||
boolean key,
|
boolean key,
|
||||||
SimpleIdMapperBuilder mapper,
|
SimpleIdMapperBuilder mapper,
|
||||||
Property mappedProperty,
|
Property mappedProperty,
|
||||||
Property virtualProperty) {
|
Property virtualProperty,
|
||||||
|
boolean audited) {
|
||||||
|
|
||||||
if ( PropertyPath.IDENTIFIER_MAPPER_PROPERTY.equals( mappedProperty.getName() ) ) {
|
if ( PropertyPath.IDENTIFIER_MAPPER_PROPERTY.equals( mappedProperty.getName() ) ) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -93,6 +96,19 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator {
|
||||||
|
|
||||||
return added;
|
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 );
|
return addBasic( attributeContainer, propertyAuditingData, mappedProperty.getValue(), mapper, key );
|
||||||
}
|
}
|
||||||
|
@ -116,7 +132,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator {
|
||||||
virtualProperty = null;
|
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 the entity is audited, and a non-supported id component is used, throw exception.
|
||||||
if ( audited ) {
|
if ( audited ) {
|
||||||
throw new EnversMappingException(
|
throw new EnversMappingException(
|
||||||
|
@ -196,7 +212,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator {
|
||||||
if ( idMapper != null ) {
|
if ( idMapper != null ) {
|
||||||
// Multiple id
|
// Multiple id
|
||||||
final Component virtualComponent = (Component) persistentClass.getIdentifier();
|
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 ) ) {
|
if ( !addIdProperties( relation, idMapper, virtualComponent, mapper, false, audited ) ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -210,8 +226,7 @@ public final class IdMetadataGenerator extends AbstractMetadataGenerator {
|
||||||
else if ( idProp.isComposite() ) {
|
else if ( idProp.isComposite() ) {
|
||||||
// Embedded id
|
// Embedded id
|
||||||
final Component idComponent = (Component) idProp.getValue();
|
final Component idComponent = (Component) idProp.getValue();
|
||||||
final Class<?> embeddableClass = loadClass( idComponent );
|
mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), idComponent );
|
||||||
mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), embeddableClass, persistentClass.getServiceRegistry() );
|
|
||||||
|
|
||||||
if ( !addIdProperties( relation, idComponent, null, mapper, false, audited ) ) {
|
if ( !addIdProperties( relation, idComponent, null, mapper, false, audited ) ) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -17,15 +17,18 @@ import org.hibernate.internal.util.ReflectHelper;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An abstract identifier mapper implementation specific for composite identifiers.
|
||||||
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractCompositeIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
|
public abstract class AbstractCompositeIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
|
||||||
protected final Class compositeIdClass;
|
protected final Class<?> compositeIdClass;
|
||||||
|
|
||||||
protected Map<PropertyData, SingleIdMapper> ids;
|
protected Map<PropertyData, AbstractIdMapper> ids;
|
||||||
|
|
||||||
protected AbstractCompositeIdMapper(Class compositeIdClass, ServiceRegistry serviceRegistry) {
|
protected AbstractCompositeIdMapper(Class<?> compositeIdClass, ServiceRegistry serviceRegistry) {
|
||||||
super( serviceRegistry );
|
super( serviceRegistry );
|
||||||
this.compositeIdClass = compositeIdClass;
|
this.compositeIdClass = compositeIdClass;
|
||||||
ids = Tools.newLinkedHashMap();
|
ids = Tools.newLinkedHashMap();
|
||||||
|
@ -33,7 +36,12 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(PropertyData propertyData) {
|
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
|
@Override
|
||||||
|
@ -43,7 +51,7 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
final Object compositeId = instantiateCompositeId();
|
final Object compositeId = instantiateCompositeId();
|
||||||
for ( SingleIdMapper mapper : ids.values() ) {
|
for ( AbstractIdMapper mapper : ids.values() ) {
|
||||||
if ( !mapper.mapToEntityFromMap( compositeId, data ) ) {
|
if ( !mapper.mapToEntityFromMap( compositeId, data ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +60,11 @@ public abstract class AbstractCompositeIdMapper extends AbstractIdMapper impleme
|
||||||
return compositeId;
|
return compositeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mapToEntityFromEntity(Object objectTo, Object objectFrom) {
|
||||||
|
// no-op; does nothing
|
||||||
|
}
|
||||||
|
|
||||||
protected Object instantiateCompositeId() {
|
protected Object instantiateCompositeId() {
|
||||||
return AccessController.doPrivileged(
|
return AccessController.doPrivileged(
|
||||||
new PrivilegedAction<Object>() {
|
new PrivilegedAction<Object>() {
|
||||||
|
|
|
@ -13,6 +13,8 @@ import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The base abstract class implementation for identifier mappers.
|
||||||
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
* @author Chris Cranford
|
* @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) {
|
private void handleNullValue(Parameters parameters, String alias, String propertyName, boolean equals) {
|
||||||
if ( equals ) {
|
if ( equals ) {
|
||||||
parameters.addNullRestriction( alias, propertyName );
|
parameters.addNullRestriction( alias, propertyName );
|
||||||
|
|
|
@ -16,19 +16,27 @@ import java.util.Map;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.PropertyData;
|
import org.hibernate.envers.internal.entities.PropertyData;
|
||||||
import org.hibernate.envers.internal.tools.ReflectionTools;
|
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.Getter;
|
||||||
import org.hibernate.property.access.spi.Setter;
|
import org.hibernate.property.access.spi.Setter;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An identifier mapper implementation for {@link jakarta.persistence.EmbeddedId} mappings.
|
||||||
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
|
public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
|
||||||
private PropertyData idPropertyData;
|
private PropertyData idPropertyData;
|
||||||
|
|
||||||
public EmbeddedIdMapper(PropertyData idPropertyData, Class compositeIdClass, ServiceRegistry serviceRegistry) {
|
public EmbeddedIdMapper(PropertyData propertyData, Component component) {
|
||||||
super( compositeIdClass, serviceRegistry );
|
super( component.getComponentClass(), component.getServiceRegistry() );
|
||||||
|
this.idPropertyData = propertyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbeddedIdMapper(PropertyData idPropertyData, Class<?> compositeIdClass, ServiceRegistry serviceRegistry) {
|
||||||
|
super( compositeIdClass, serviceRegistry );
|
||||||
this.idPropertyData = idPropertyData;
|
this.idPropertyData = idPropertyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Base contract for all identifier mappers.
|
||||||
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,14 +13,23 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.envers.internal.entities.PropertyData;
|
import org.hibernate.envers.internal.entities.PropertyData;
|
||||||
|
import org.hibernate.mapping.Component;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
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 Adam Warski (adam at warski dot org)
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class MultipleIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
|
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 );
|
super( compositeIdClass, serviceRegistry );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +41,9 @@ public class MultipleIdMapper extends AbstractCompositeIdMapper implements Simpl
|
||||||
@Override
|
@Override
|
||||||
public void mapToMapFromId(Session session, Map<String, Object> data, Object obj) {
|
public void mapToMapFromId(Session session, Map<String, Object> data, Object obj) {
|
||||||
if ( compositeIdClass.isInstance( obj ) ) {
|
if ( compositeIdClass.isInstance( obj ) ) {
|
||||||
for ( Map.Entry<PropertyData, SingleIdMapper> entry : ids.entrySet() ) {
|
for ( Map.Entry<PropertyData, AbstractIdMapper> entry : ids.entrySet() ) {
|
||||||
final PropertyData propertyData = entry.getKey();
|
final PropertyData propertyData = entry.getKey();
|
||||||
final SingleIdMapper idMapper = entry.getValue();
|
final AbstractIdMapper idMapper = entry.getValue();
|
||||||
|
|
||||||
if ( propertyData.getVirtualReturnClass() == null ) {
|
if ( propertyData.getVirtualReturnClass() == null ) {
|
||||||
idMapper.mapToMapFromEntity( data, obj );
|
idMapper.mapToMapFromEntity( data, obj );
|
||||||
|
@ -92,7 +101,7 @@ public class MultipleIdMapper extends AbstractCompositeIdMapper implements Simpl
|
||||||
}
|
}
|
||||||
|
|
||||||
final Object compositeId = instantiateCompositeId();
|
final Object compositeId = instantiateCompositeId();
|
||||||
for ( SingleIdMapper mapper : ids.values() ) {
|
for ( AbstractIdMapper mapper : ids.values() ) {
|
||||||
mapper.mapToEntityFromEntity( compositeId, data );
|
mapper.mapToEntityFromEntity( compositeId, data );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,21 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.envers.internal.entities.mapper.id;
|
package org.hibernate.envers.internal.entities.mapper.id;
|
||||||
|
|
||||||
|
import org.hibernate.envers.internal.entities.PropertyData;
|
||||||
import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
|
import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A simple identifier builder contract.
|
||||||
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public interface SimpleIdMapperBuilder extends IdMapper, SimpleMapperBuilder {
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@ import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
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 Adam Warski (adam at warski dot org)
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
|
public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
|
||||||
private PropertyData propertyData;
|
private PropertyData propertyData;
|
||||||
|
@ -44,6 +47,11 @@ public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBu
|
||||||
this.propertyData = propertyData;
|
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
|
@Override
|
||||||
public boolean mapToEntityFromMap(final Object obj, Map data) {
|
public boolean mapToEntityFromMap(final Object obj, Map data) {
|
||||||
if ( data == null || obj == null ) {
|
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) {
|
public void mapToEntityFromEntity(final Object objTo, final Object objFrom) {
|
||||||
if ( objTo == null || objFrom == null ) {
|
if ( objTo == null || objFrom == null ) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<OwnerOfRelationCode> ownerOfRelationCodes = new java.util.HashSet<>();
|
||||||
|
|
||||||
|
public CompositeEntityId getCodeObject() {
|
||||||
|
return codeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeObject(CompositeEntityId codeObject) {
|
||||||
|
this.codeObject = codeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<OwnerOfRelationCode> getOwnerOfRelationCodes() {
|
||||||
|
return ownerOfRelationCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwnerOfRelationCodes(Set<OwnerOfRelationCode> 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue