org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-envers Show documentation
Show all versions of hibernate-envers Show documentation
Hibernate's entity version (audit/history) support
/*
* 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.configuration.internal.metadata;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.configuration.internal.metadata.reader.ClassAuditingData;
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.internal.EnversMessageLogger;
import org.hibernate.envers.internal.entities.EntityConfiguration;
import org.hibernate.envers.internal.entities.IdMappingData;
import org.hibernate.envers.internal.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.internal.entities.mapper.ExtendedPropertyMapper;
import org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper;
import org.hibernate.envers.internal.entities.mapper.SubclassPropertyMapper;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.envers.internal.tools.Triple;
import org.hibernate.envers.strategy.AuditStrategy;
import org.hibernate.envers.strategy.spi.MappingContext;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SyntheticProperty;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tuple.GeneratedValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.OneToOneType;
import org.hibernate.type.Type;
import org.dom4j.Element;
import org.jboss.logging.Logger;
/**
* @author Adam Warski (adam at warski dot org)
* @author Sebastian Komander
* @author Tomasz Bech
* @author Stephanie Pau at Markit Group Plc
* @author Hernán Chanfreau
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Michal Skowronek (mskowr at o2 dot pl)
* @author Chris Cranford
*/
public final class AuditMetadataGenerator {
private static final EnversMessageLogger LOG = Logger.getMessageLogger(
EnversMessageLogger.class,
AuditMetadataGenerator.class.getName()
);
private final MetadataImplementor metadata;
private final ServiceRegistry serviceRegistry;
private final GlobalConfiguration globalCfg;
private final AuditEntitiesConfiguration verEntCfg;
private final AuditStrategy auditStrategy;
private final Element revisionInfoRelationMapping;
private final ClassLoaderService classLoaderService;
/*
* Generators for different kinds of property values/types.
*/
private final BasicMetadataGenerator basicMetadataGenerator;
private final ComponentMetadataGenerator componentMetadataGenerator;
private final IdMetadataGenerator idMetadataGenerator;
private final ToOneRelationMetadataGenerator toOneRelationMetadataGenerator;
/*
* Here information about already generated mappings will be accumulated.
*/
private final Map entitiesConfigurations;
private final Map notAuditedEntitiesConfigurations;
private final AuditEntityNameRegister auditEntityNameRegister;
// Map entity name -> (join descriptor -> element describing the "versioned" join)
private final Map> entitiesJoins;
public AuditMetadataGenerator(
MetadataImplementor metadata,
ServiceRegistry serviceRegistry,
GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
AuditStrategy auditStrategy,
Element revisionInfoRelationMapping,
AuditEntityNameRegister auditEntityNameRegister) {
this.metadata = metadata;
this.serviceRegistry = serviceRegistry;
this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg;
this.auditStrategy = auditStrategy;
this.revisionInfoRelationMapping = revisionInfoRelationMapping;
this.basicMetadataGenerator = new BasicMetadataGenerator( this );
this.componentMetadataGenerator = new ComponentMetadataGenerator( this );
this.idMetadataGenerator = new IdMetadataGenerator( this );
this.toOneRelationMetadataGenerator = new ToOneRelationMetadataGenerator( this );
this.auditEntityNameRegister = auditEntityNameRegister;
entitiesConfigurations = new HashMap<>();
notAuditedEntitiesConfigurations = new HashMap<>();
entitiesJoins = new HashMap<>();
classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
}
public MetadataImplementor getMetadata() {
return metadata;
}
public ServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
public ClassLoaderService getClassLoaderService() {
return classLoaderService;
}
/**
* Clones the revision info relation mapping, so that it can be added to other mappings. Also, the name of
* the property and the column are set properly.
*
* @return A revision info mapping, which can be added to other mappings (has no parent).
*/
private Element cloneAndSetupRevisionInfoRelationMapping() {
final Element revMapping = (Element) revisionInfoRelationMapping.clone();
revMapping.addAttribute( "name", verEntCfg.getRevisionFieldName() );
if ( globalCfg.isCascadeDeleteRevision() ) {
revMapping.addAttribute( "on-delete", "cascade" );
}
MetadataTools.addOrModifyColumn( revMapping, verEntCfg.getRevisionFieldName() );
return revMapping;
}
void addRevisionInfoRelation(Element anyMapping) {
anyMapping.add( cloneAndSetupRevisionInfoRelationMapping() );
}
void addRevisionType(Element anyMapping, Element anyMappingEnd) {
addRevisionType( anyMapping, anyMappingEnd, false );
}
void addRevisionType(Element anyMapping, Element anyMappingEnd, boolean isKey) {
final Element revTypeProperty = MetadataTools.addProperty(
anyMapping,
verEntCfg.getRevisionTypePropName(),
verEntCfg.getRevisionTypePropType(),
true,
isKey
);
revTypeProperty.addAttribute( "type", "org.hibernate.envers.internal.entities.RevisionTypeType" );
}
void addAdditionalColumns(Element anyMapping) {
auditStrategy.addAdditionalColumns( new MappingContext( anyMapping, revisionInfoRelationMapping, verEntCfg ) );
}
private void addValueInFirstPass(
Element parent,
Value value,
CompositeMapperBuilder currentMapper,
String entityName,
EntityXmlMappingData xmlMappingData,
PropertyAuditingData propertyAuditingData,
boolean insertable,
boolean processModifiedFlag) {
final Type type = value.getType();
final boolean isBasic = basicMetadataGenerator.addBasic(
parent,
propertyAuditingData,
value,
currentMapper,
insertable,
false
);
if ( isBasic ) {
// The property was mapped by the basic generator.
}
else if ( type instanceof ComponentType ) {
componentMetadataGenerator.addComponent(
parent, propertyAuditingData, value, currentMapper,
entityName, xmlMappingData, true
);
}
else {
if ( !processedInSecondPass( type ) ) {
// If we got here in the first pass, it means the basic mapper didn't map it, and none of the
// above branches either.
throwUnsupportedTypeException( type, entityName, propertyAuditingData.getName() );
}
return;
}
addModifiedFlagIfNeeded( value, parent, propertyAuditingData, processModifiedFlag );
}
private boolean processedInSecondPass(Type type) {
return type instanceof ComponentType || type instanceof ManyToOneType ||
type instanceof OneToOneType || type instanceof CollectionType;
}
private void addValueInSecondPass(
Element parent,
Value value,
CompositeMapperBuilder currentMapper,
String entityName,
EntityXmlMappingData xmlMappingData,
PropertyAuditingData propertyAuditingData,
boolean insertable,
boolean processModifiedFlag) {
final Type type = value.getType();
if ( type instanceof ComponentType ) {
componentMetadataGenerator.addComponent(
parent,
propertyAuditingData,
value,
currentMapper,
entityName,
xmlMappingData,
false
);
// mod flag field has been already generated in first pass
return;
}
else if ( type instanceof ManyToOneType ) {
toOneRelationMetadataGenerator.addToOne(
parent,
propertyAuditingData,
value,
currentMapper,
entityName,
insertable
);
}
else if ( type instanceof OneToOneType ) {
final OneToOne oneToOne = (OneToOne) value;
if ( oneToOne.getReferencedPropertyName() != null ) {
toOneRelationMetadataGenerator.addOneToOneNotOwning(
propertyAuditingData,
value,
currentMapper,
entityName
);
}
else {
// @OneToOne relation marked with @PrimaryKeyJoinColumn
toOneRelationMetadataGenerator.addOneToOnePrimaryKeyJoinColumn(
propertyAuditingData,
value,
currentMapper,
entityName,
insertable
);
}
}
else if ( type instanceof CollectionType ) {
final CollectionMetadataGenerator collectionMetadataGenerator = new CollectionMetadataGenerator(
this,
(Collection) value,
currentMapper,
entityName,
xmlMappingData,
propertyAuditingData
);
collectionMetadataGenerator.addCollection();
}
else {
return;
}
addModifiedFlagIfNeeded( value, parent, propertyAuditingData, processModifiedFlag );
}
private void addModifiedFlagIfNeeded(
Value value,
Element parent,
PropertyAuditingData propertyAuditingData,
boolean processModifiedFlag) {
if ( processModifiedFlag && propertyAuditingData.isUsingModifiedFlag() ) {
globalCfg.getModifiedColumnNamingStrategy().addModifiedColumns(
globalCfg,
value,
parent,
propertyAuditingData
);
}
}
void addValue(
Element parent, Value value, CompositeMapperBuilder currentMapper, String entityName,
EntityXmlMappingData xmlMappingData, PropertyAuditingData propertyAuditingData,
boolean insertable, boolean firstPass, boolean processModifiedFlag) {
if ( firstPass ) {
addValueInFirstPass(
parent, value, currentMapper, entityName,
xmlMappingData, propertyAuditingData, insertable, processModifiedFlag
);
}
else {
addValueInSecondPass(
parent, value, currentMapper, entityName,
xmlMappingData, propertyAuditingData, insertable, processModifiedFlag
);
}
}
private void addProperties(
Element parent,
Iterator properties,
CompositeMapperBuilder currentMapper,
ClassAuditingData auditingData,
String entityName,
EntityXmlMappingData xmlMappingData,
boolean firstPass) {
while ( properties.hasNext() ) {
final Property property = properties.next();
final String propertyName = property.getName();
final PropertyAuditingData propertyAuditingData = auditingData.getPropertyAuditingData( propertyName );
if ( propertyAuditingData != null ) {
// HHH-10246
// Verifies if a mapping exists using a @JoinColumn against a @NaturalId field
// and if so, this eliminates the mapping property as it isn't needed.
if ( property instanceof SyntheticProperty ) {
if ( property.getValue().isAlternateUniqueKey() ) {
continue;
}
}
addValue(
parent,
property.getValue(),
currentMapper,
entityName,
xmlMappingData,
propertyAuditingData,
isPropertyInsertable( property ),
firstPass,
true
);
}
}
}
private boolean isPropertyInsertable(Property property) {
if ( !property.isInsertable() ) {
final ValueGeneration generation = property.getValueGenerationStrategy();
if ( generation instanceof GeneratedValueGeneration ) {
final GeneratedValueGeneration valueGeneration = (GeneratedValueGeneration) generation;
if ( GenerationTiming.INSERT == valueGeneration.getGenerationTiming()
|| GenerationTiming.ALWAYS == valueGeneration.getGenerationTiming() ) {
return true;
}
}
}
return property.isInsertable();
}
private boolean checkPropertiesAudited(Iterator properties, ClassAuditingData auditingData) {
while ( properties.hasNext() ) {
final Property property = properties.next();
final String propertyName = property.getName();
final PropertyAuditingData propertyAuditingData = auditingData.getPropertyAuditingData( propertyName );
if ( propertyAuditingData == null ) {
return false;
}
}
return true;
}
protected String getSchema(String schemaFromAnnotation, Table table) {
// Get the schema from the annotation ...
String schema = schemaFromAnnotation;
// ... if empty, try using the default ...
if ( StringTools.isEmpty( schema ) ) {
schema = globalCfg.getDefaultSchemaName();
// ... if still empty, use the same as the normal table.
if ( StringTools.isEmpty( schema ) ) {
schema = table.getSchema();
}
}
return schema;
}
protected String getCatalog(String catalogFromAnnotation, Table table) {
// Get the catalog from the annotation ...
String catalog = catalogFromAnnotation;
// ... if empty, try using the default ...
if ( StringTools.isEmpty( catalog ) ) {
catalog = globalCfg.getDefaultCatalogName();
// ... if still empty, use the same as the normal table.
if ( StringTools.isEmpty( catalog ) ) {
catalog = table.getCatalog();
}
}
return catalog;
}
@SuppressWarnings({"unchecked"})
private void createJoins(PersistentClass pc, Element parent, ClassAuditingData auditingData) {
final Iterator joins = pc.getJoinIterator();
final Map joinElements = new HashMap<>();
entitiesJoins.put( pc.getEntityName(), joinElements );
while ( joins.hasNext() ) {
Join join = joins.next();
// Checking if all of the join properties are audited
if ( !checkPropertiesAudited( join.getPropertyIterator(), auditingData ) ) {
continue;
}
// Determining the table name. If there is no entry in the dictionary, just constructing the table name
// as if it was an entity (by appending/prepending configured strings).
final String originalTableName = join.getTable().getName();
String auditTableName = auditingData.getSecondaryTableDictionary().get( originalTableName );
if ( auditTableName == null ) {
auditTableName = verEntCfg.getAuditEntityName( originalTableName );
}
final String schema = getSchema( auditingData.getAuditTable().schema(), join.getTable() );
final String catalog = getCatalog( auditingData.getAuditTable().catalog(), join.getTable() );
final Element joinElement = MetadataTools.createJoin( parent, auditTableName, schema, catalog );
joinElements.put( join, joinElement );
// HHH-8305 - Fix case when join is considered optional.
if ( join.isOptional() ) {
joinElement.addAttribute( "optional", "true" );
}
// HHH-8305 - Fix case when join is the inverse side of a mapping.
if ( join.isInverse() ) {
joinElement.addAttribute( "inverse", "true" );
}
final Element joinKey = joinElement.addElement( "key" );
MetadataTools.addColumns( joinKey, join.getKey().getColumnIterator() );
MetadataTools.addColumn( joinKey, verEntCfg.getRevisionFieldName(), null, null, null, null, null, null );
}
}
@SuppressWarnings({"unchecked"})
private void addJoins(
PersistentClass pc,
CompositeMapperBuilder currentMapper,
ClassAuditingData auditingData,
String entityName,
EntityXmlMappingData xmlMappingData,
boolean firstPass) {
final Iterator joins = pc.getJoinIterator();
while ( joins.hasNext() ) {
final Join join = joins.next();
final Element joinElement = entitiesJoins.get( entityName ).get( join );
if ( joinElement != null ) {
addProperties(
joinElement,
join.getPropertyIterator(),
currentMapper,
auditingData,
entityName,
xmlMappingData,
firstPass
);
}
}
}
@SuppressWarnings({"unchecked"})
private Triple generateMappingData(
PersistentClass pc, EntityXmlMappingData xmlMappingData, AuditTableData auditTableData,
IdMappingData idMapper) {
final Element classMapping = MetadataTools.createEntity(
xmlMappingData.getMainXmlMapping(),
auditTableData,
pc.getDiscriminatorValue(),
pc.isAbstract()
);
final ExtendedPropertyMapper propertyMapper = new MultiPropertyMapper();
// Checking if there is a discriminator column
if ( pc.getDiscriminator() != null ) {
final Element discriminatorElement = classMapping.addElement( "discriminator" );
// Database column or SQL formula allowed to distinguish entity types
MetadataTools.addColumnsOrFormulas( discriminatorElement, pc.getDiscriminator().getColumnIterator() );
discriminatorElement.addAttribute( "type", pc.getDiscriminator().getType().getName() );
}
// Adding the id mapping
classMapping.add( (Element) idMapper.getXmlMapping().clone() );
// Adding the "revision type" property
addRevisionType( classMapping, classMapping );
addAdditionalColumns( classMapping );
return Triple.make( classMapping, propertyMapper, null );
}
private Triple generateInheritanceMappingData(
PersistentClass pc, EntityXmlMappingData xmlMappingData, AuditTableData auditTableData,
String inheritanceMappingType) {
final String extendsEntityName = verEntCfg.getAuditEntityName( pc.getSuperclass().getEntityName() );
final Element classMapping = MetadataTools.createSubclassEntity(
xmlMappingData.getMainXmlMapping(),
inheritanceMappingType,
auditTableData,
extendsEntityName,
pc.getDiscriminatorValue(),
pc.isAbstract()
);
// The id and revision type is already mapped in the parent
// Getting the property mapper of the parent - when mapping properties, they need to be included
final String parentEntityName = pc.getSuperclass().getEntityName();
final EntityConfiguration parentConfiguration = entitiesConfigurations.get( parentEntityName );
if ( parentConfiguration == null ) {
throw new MappingException(
"Entity '" + pc.getEntityName() + "' is audited, but its superclass: '" +
parentEntityName + "' is not."
);
}
final ExtendedPropertyMapper parentPropertyMapper = parentConfiguration.getPropertyMapper();
final ExtendedPropertyMapper propertyMapper = new SubclassPropertyMapper(
new MultiPropertyMapper(),
parentPropertyMapper
);
return Triple.make( classMapping, propertyMapper, parentEntityName );
}
@SuppressWarnings({"unchecked"})
public void generateFirstPass(
PersistentClass pc,
ClassAuditingData auditingData,
EntityXmlMappingData xmlMappingData,
boolean isAudited) {
final String schema = getSchema( auditingData.getAuditTable().schema(), pc.getTable() );
final String catalog = getCatalog( auditingData.getAuditTable().catalog(), pc.getTable() );
if ( !isAudited ) {
final String entityName = pc.getEntityName();
final IdMappingData idMapper = idMetadataGenerator.addId( pc, false );
if ( idMapper == null ) {
// Unsupported id mapping, e.g. key-many-to-one. If the entity is used in auditing, an exception
// will be thrown later on.
LOG.debugf(
"Unable to create auditing id mapping for entity %s, because of an unsupported Hibernate id mapping (e.g. key-many-to-one)",
entityName
);
return;
}
final ExtendedPropertyMapper propertyMapper = null;
final String parentEntityName = null;
final EntityConfiguration entityCfg = new EntityConfiguration(
entityName,
pc.getClassName(),
idMapper,
propertyMapper,
parentEntityName
);
notAuditedEntitiesConfigurations.put( entityName, entityCfg );
return;
}
final String entityName = pc.getEntityName();
LOG.debugf( "Generating first-pass auditing mapping for entity %s", entityName );
final String auditEntityName = verEntCfg.getAuditEntityName( entityName );
final String auditTableName = verEntCfg.getAuditTableName( entityName, pc.getTable().getName() );
// Registering the audit entity name, now that it is known
auditEntityNameRegister.register( auditEntityName );
final AuditTableData auditTableData = new AuditTableData( auditEntityName, auditTableName, schema, catalog );
// Generating a mapping for the id
final IdMappingData idMapper = idMetadataGenerator.addId( pc, true );
final InheritanceType inheritanceType = InheritanceType.get( pc );
// These properties will be read from the mapping data
final Element classMapping;
final ExtendedPropertyMapper propertyMapper;
final String parentEntityName;
final Triple mappingData;
// Reading the mapping data depending on inheritance type (if any)
switch ( inheritanceType ) {
case NONE:
mappingData = generateMappingData( pc, xmlMappingData, auditTableData, idMapper );
break;
case SINGLE:
mappingData = generateInheritanceMappingData( pc, xmlMappingData, auditTableData, "subclass" );
break;
case JOINED:
mappingData = generateInheritanceMappingData( pc, xmlMappingData, auditTableData, "joined-subclass" );
// Adding the "key" element with all id columns...
final Element keyMapping = mappingData.getFirst().addElement( "key" );
MetadataTools.addColumns( keyMapping, pc.getTable().getPrimaryKey().columnIterator() );
// ... and the revision number column, read from the revision info relation mapping.
keyMapping.add( (Element) cloneAndSetupRevisionInfoRelationMapping().element( "column" ).clone() );
break;
case TABLE_PER_CLASS:
mappingData = generateInheritanceMappingData( pc, xmlMappingData, auditTableData, "union-subclass" );
break;
default:
throw new AssertionError( "Impossible enum value." );
}
classMapping = mappingData.getFirst();
propertyMapper = mappingData.getSecond();
parentEntityName = mappingData.getThird();
xmlMappingData.setClassMapping( classMapping );
// Mapping unjoined properties
addProperties(
classMapping, pc.getUnjoinedPropertyIterator(), propertyMapper,
auditingData, pc.getEntityName(), xmlMappingData,
true
);
// Creating and mapping joins (first pass)
createJoins( pc, classMapping, auditingData );
addJoins( pc, propertyMapper, auditingData, pc.getEntityName(), xmlMappingData, true );
// HHH-7940 - New synthetic property support for @IndexColumn/@OrderColumn dynamic properties
addSynthetics( classMapping, auditingData, propertyMapper, xmlMappingData, pc.getEntityName(), true );
// Storing the generated configuration
final EntityConfiguration entityCfg = new EntityConfiguration(
auditEntityName,
pc.getClassName(),
idMapper,
propertyMapper,
parentEntityName
);
entitiesConfigurations.put( pc.getEntityName(), entityCfg );
}
private void addSynthetics(
Element classMapping,
ClassAuditingData auditingData,
CompositeMapperBuilder currentMapper,
EntityXmlMappingData xmlMappingData,
String entityName,
boolean firstPass) {
for ( PropertyAuditingData propertyAuditingData : auditingData.getSyntheticProperties() ) {
addValue(
classMapping,
propertyAuditingData.getValue(),
currentMapper,
entityName,
xmlMappingData,
propertyAuditingData,
true,
firstPass,
false
);
}
}
@SuppressWarnings({"unchecked"})
public void generateSecondPass(
PersistentClass pc,
ClassAuditingData auditingData,
EntityXmlMappingData xmlMappingData) {
final String entityName = pc.getEntityName();
LOG.debugf( "Generating second-pass auditing mapping for entity %s", entityName );
final CompositeMapperBuilder propertyMapper = entitiesConfigurations.get( entityName ).getPropertyMapper();
// Mapping unjoined properties
final Element parent = xmlMappingData.getClassMapping();
// HHH-11748 - Generate a second pass for identifiers
// This is useful for situations where @Id point to @ManyToOne and @OneToOne associations.
idMetadataGenerator.generateSecondPass( entityName, pc );
addProperties(
parent,
pc.getUnjoinedPropertyIterator(),
propertyMapper,
auditingData,
entityName,
xmlMappingData,
false
);
// Mapping joins (second pass)
addJoins( pc, propertyMapper, auditingData, entityName, xmlMappingData, false );
}
public Map getEntitiesConfigurations() {
return entitiesConfigurations;
}
// Getters for generators and configuration
BasicMetadataGenerator getBasicMetadataGenerator() {
return basicMetadataGenerator;
}
GlobalConfiguration getGlobalCfg() {
return globalCfg;
}
AuditEntitiesConfiguration getVerEntCfg() {
return verEntCfg;
}
AuditStrategy getAuditStrategy() {
return auditStrategy;
}
AuditEntityNameRegister getAuditEntityNameRegister() {
return auditEntityNameRegister;
}
void throwUnsupportedTypeException(Type type, String entityName, String propertyName) {
final String message = "Type not supported for auditing: " + type.getClass().getName() +
", on entity " + entityName + ", property '" + propertyName + "'.";
throw new MappingException( message );
}
/**
* Reads the id mapping data of a referenced entity.
*
* @param entityName Name of the entity which is the source of the relation.
* @param referencedEntityName Name of the entity which is the target of the relation.
* @param propertyAuditingData Auditing data of the property that is the source of the relation.
* @param allowNotAuditedTarget Are not-audited target entities allowed.
*
* @return The id mapping data of the related entity.
*
* @throws MappingException If a relation from an audited to a non-audited entity is detected, which is not
* mapped using {@link RelationTargetAuditMode#NOT_AUDITED}.
*/
IdMappingData getReferencedIdMappingData(
String entityName, String referencedEntityName,
PropertyAuditingData propertyAuditingData,
boolean allowNotAuditedTarget) {
EntityConfiguration configuration = getEntitiesConfigurations().get( referencedEntityName );
if ( configuration == null ) {
final RelationTargetAuditMode relationTargetAuditMode = propertyAuditingData.getRelationTargetAuditMode();
configuration = getNotAuditedEntitiesConfigurations().get( referencedEntityName );
if ( configuration == null || !allowNotAuditedTarget || !RelationTargetAuditMode.NOT_AUDITED.equals(
relationTargetAuditMode
) ) {
throw new MappingException(
"An audited relation from " + entityName + "."
+ propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!"
+ (allowNotAuditedTarget ?
" Such mapping is possible, but has to be explicitly defined using @Audited(targetAuditMode = NOT_AUDITED)." :
"")
);
}
}
return configuration.getIdMappingData();
}
/**
* Get the notAuditedEntitiesConfigurations property.
*
* @return the notAuditedEntitiesConfigurations property value
*/
public Map getNotAuditedEntitiesConfigurations() {
return notAuditedEntitiesConfigurations;
}
}