org.hibernate.envers.configuration.internal.metadata.CollectionMetadataGenerator 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.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.persistence.JoinColumn;
import org.dom4j.Element;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.internal.metadata.reader.AuditedPropertiesReader;
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditedPropertiesReader;
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditingData;
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.PropertyData;
import org.hibernate.envers.internal.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper;
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
import org.hibernate.envers.internal.entities.mapper.SinglePropertyMapper;
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
import org.hibernate.envers.internal.entities.mapper.relation.BasicCollectionMapper;
import org.hibernate.envers.internal.entities.mapper.relation.CommonCollectionMapperData;
import org.hibernate.envers.internal.entities.mapper.relation.ListCollectionMapper;
import org.hibernate.envers.internal.entities.mapper.relation.MapCollectionMapper;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.internal.entities.mapper.relation.SortedMapCollectionMapper;
import org.hibernate.envers.internal.entities.mapper.relation.SortedSetCollectionMapper;
import org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleDummyComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleEmbeddableComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapElementNotKeyComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapKeyIdComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleMapKeyPropertyComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleRelatedComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleSimpleComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.component.MiddleStraightComponentMapper;
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.ListProxy;
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.MapProxy;
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SetProxy;
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SortedMapProxy;
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SortedSetProxy;
import org.hibernate.envers.internal.entities.mapper.relation.query.OneAuditEntityQueryGenerator;
import org.hibernate.envers.internal.entities.mapper.relation.query.RelationQueryGenerator;
import org.hibernate.envers.internal.tools.MappingTools;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.envers.internal.tools.Tools;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.type.BagType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.ListType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.MapType;
import org.hibernate.type.MaterializedClobType;
import org.hibernate.type.MaterializedNClobType;
import org.hibernate.type.SetType;
import org.hibernate.type.SortedMapType;
import org.hibernate.type.SortedSetType;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* Generates metadata for a collection-valued property.
*
* @author Adam Warski (adam at warski dot org)
* @author HernпїЅn Chanfreau
* @author Chris Cranford
*/
public final class CollectionMetadataGenerator {
// todo: this class should undergo major changes to differentiate between various collection types.
private static final EnversMessageLogger LOG = Logger.getMessageLogger(
EnversMessageLogger.class,
CollectionMetadataGenerator.class.getName()
);
private final AuditMetadataGenerator mainGenerator;
private final String propertyName;
private final Collection propertyValue;
private final CompositeMapperBuilder currentMapper;
private final String referencingEntityName;
private final EntityXmlMappingData xmlMappingData;
private final PropertyAuditingData propertyAuditingData;
private final EntityConfiguration referencingEntityConfiguration;
/**
* Null if this collection isn't a relation to another entity.
*/
private final String referencedEntityName;
/**
* @param mainGenerator Main generator, giving access to configuration and the basic mapper.
* @param propertyValue Value of the collection, as mapped by Hibernate.
* @param currentMapper Mapper, to which the appropriate {@link PropertyMapper} will be added.
* @param referencingEntityName Name of the entity that owns this collection.
* @param xmlMappingData In case this collection requires a middle table, additional mapping documents will
* be created using this object.
* @param propertyAuditingData Property auditing (meta-)data. Among other things, holds the name of the
* property that references the collection in the referencing entity, the user data for middle (join)
* table and the value of the @MapKey
annotation, if there was one.
*/
public CollectionMetadataGenerator(
AuditMetadataGenerator mainGenerator,
Collection propertyValue,
CompositeMapperBuilder currentMapper,
String referencingEntityName,
EntityXmlMappingData xmlMappingData,
PropertyAuditingData propertyAuditingData) {
this.mainGenerator = mainGenerator;
this.propertyValue = propertyValue;
this.currentMapper = currentMapper;
this.referencingEntityName = referencingEntityName;
this.xmlMappingData = xmlMappingData;
this.propertyAuditingData = propertyAuditingData;
this.propertyName = propertyAuditingData.getName();
referencingEntityConfiguration = mainGenerator.getEntitiesConfigurations().get( referencingEntityName );
if ( referencingEntityConfiguration == null ) {
throw new MappingException( "Unable to read auditing configuration for " + referencingEntityName + "!" );
}
referencedEntityName = MappingTools.getReferencedEntityName( propertyValue.getElement() );
}
void addCollection() {
final Type type = propertyValue.getType();
final Value value = propertyValue.getElement();
final boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType;
final boolean inverseOneToMany = (value instanceof OneToMany) && (propertyValue.isInverse());
final boolean owningManyToOneWithJoinTableBidirectional = (value instanceof ManyToOne) && (propertyAuditingData.getRelationMappedBy() != null);
final boolean fakeOneToManyBidirectional = (value instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null);
if ( oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional || owningManyToOneWithJoinTableBidirectional) ) {
// A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...")
addOneToManyAttached( fakeOneToManyBidirectional );
}
else {
// All other kinds of relations require a middle (join) table.
addWithMiddleTable();
}
}
private MiddleIdData createMiddleIdData(IdMappingData idMappingData, String prefix, String entityName) {
return new MiddleIdData(
mainGenerator.getVerEntCfg(),
idMappingData,
prefix,
entityName,
mainGenerator.getEntitiesConfigurations().containsKey( entityName )
);
}
@SuppressWarnings({"unchecked"})
private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
LOG.debugf(
"Adding audit mapping for property %s.%s: one-to-many collection, using a join column on the referenced entity",
referencingEntityName,
propertyName
);
// check whether the property has an @IndexColumn or @OrderColumn because its part of an
// IndexedCollection mapping type.
final boolean indexed = ( propertyValue instanceof IndexedCollection ) && ( (IndexedCollection) propertyValue ).getIndex() != null;
final String mappedBy = getMappedBy( propertyValue );
final IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(
referencingEntityName,
referencedEntityName,
propertyAuditingData,
false
);
final IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
// Generating the id mappers data for the referencing side of the relation.
final MiddleIdData referencingIdData = createMiddleIdData(
referencingIdMapping,
mappedBy + "_",
referencingEntityName
);
// And for the referenced side. The prefixed mapper won't be used (as this collection isn't persisted
// in a join table, so the prefix value is arbitrary).
final MiddleIdData referencedIdData = createMiddleIdData(
referencedIdMapping,
null, referencedEntityName
);
// Generating the element mapping.
final MiddleComponentData elementComponentData = new MiddleComponentData(
new MiddleRelatedComponentMapper( referencedIdData ), 0
);
// Generating the index mapping, if an index exists. It can only exists in case a javax.persistence.MapKey
// annotation is present on the entity. So the middleEntityXml will be not be used. The queryGeneratorBuilder
// will only be checked for nullnes.
MiddleComponentData indexComponentData = addIndex( null, null );
// Generating the query generator - it should read directly from the related entity.
final RelationQueryGenerator queryGenerator = new OneAuditEntityQueryGenerator(
mainGenerator.getGlobalCfg(),
mainGenerator.getVerEntCfg(),
mainGenerator.getAuditStrategy(),
referencingIdData,
referencedEntityName,
referencedIdData,
isEmbeddableElementType(),
mappedBy,
isMappedByKey( propertyValue, mappedBy ),
propertyValue.getOrderBy()
);
// Creating common mapper data.
final CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
mainGenerator.getVerEntCfg(),
referencedEntityName,
propertyAuditingData.getPropertyData(),
referencingIdData,
queryGenerator,
propertyValue.getRole()
);
PropertyMapper fakeBidirectionalRelationMapper;
PropertyMapper fakeBidirectionalRelationIndexMapper;
if ( fakeOneToManyBidirectional || indexed ) {
// In case of a fake many-to-one bidirectional relation, we have to generate a mapper which maps
// the mapped-by property name to the id of the related entity (which is the owner of the collection).
final String auditMappedBy;
if ( fakeOneToManyBidirectional ) {
auditMappedBy = propertyAuditingData.getAuditMappedBy();
}
else {
auditMappedBy = propertyValue.getMappedByProperty();
}
// Creating a prefixed relation mapper.
final IdMapper relMapper = referencingIdMapping.getIdMapper().prefixMappedProperties(
MappingTools.createToOneRelationPrefix( auditMappedBy )
);
fakeBidirectionalRelationMapper = new ToOneIdMapper(
relMapper,
// The mapper will only be used to map from entity to map, so no need to provide other details
// when constructing the PropertyData.
new PropertyData( auditMappedBy, null, null, null ),
referencingEntityName, false
);
final String positionMappedBy;
if ( fakeOneToManyBidirectional ) {
positionMappedBy = propertyAuditingData.getPositionMappedBy();
}
else if ( indexed ) {
final Value indexValue = ( (IndexedCollection) propertyValue ).getIndex();
positionMappedBy = indexValue.getColumnIterator().next().getText();
}
else {
positionMappedBy = null;
}
// Checking if there's an index defined. If so, adding a mapper for it.
if ( positionMappedBy != null ) {
final Type indexType;
if ( IndexedCollection.class.isInstance( propertyValue ) ) {
indexType = ( (IndexedCollection) propertyValue ).getIndex().getType();
}
else {
// todo - do we need to reverse lookup the type anyway?
indexType = null;
}
fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper(
PropertyData.forProperty( positionMappedBy, indexType )
);
// Also, overwriting the index component data to properly read the index.
indexComponentData = new MiddleComponentData(
new MiddleStraightComponentMapper( positionMappedBy ),
0
);
}
else {
fakeBidirectionalRelationIndexMapper = null;
}
}
else {
fakeBidirectionalRelationMapper = null;
fakeBidirectionalRelationIndexMapper = null;
}
// Checking the type of the collection and adding an appropriate mapper.
addMapper( commonCollectionMapperData, elementComponentData, indexComponentData );
// Storing information about this relation.
referencingEntityConfiguration.addToManyNotOwningRelation(
propertyName,
mappedBy,
referencedEntityName,
referencingIdData.getPrefixedMapper(),
fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper,
indexed
);
}
/**
* Adds mapping of the id of a related entity to the given xml mapping, prefixing the id with the given prefix.
*
* @param xmlMapping Mapping, to which to add the xml.
* @param prefix Prefix for the names of properties which will be prepended to properties that form the id.
* @param columnNameIterator Iterator over the column names that will be used for properties that form the id.
* @param relatedIdMapping Id mapping data of the related entity.
*/
@SuppressWarnings({"unchecked"})
private void addRelatedToXmlMapping(
Element xmlMapping,
String prefix,
MetadataTools.ColumnNameIterator columnNameIterator,
IdMappingData relatedIdMapping) {
final Element properties = (Element) relatedIdMapping.getXmlRelationMapping().clone();
MetadataTools.prefixNamesInPropertyElement( properties, prefix, columnNameIterator, true, true );
for ( Element idProperty : (java.util.List) properties.elements() ) {
xmlMapping.add( (Element) idProperty.clone() );
}
}
private String getMiddleTableName(Collection value, String entityName) {
// We check how Hibernate maps the collection.
if ( value.getElement() instanceof OneToMany && !value.isInverse() ) {
// This must be a @JoinColumn+@OneToMany mapping. Generating the table name, as Hibernate doesn't use a
// middle table for mapping this relation.
return StringTools.getLastComponent( entityName ) + "_" + StringTools.getLastComponent(
MappingTools.getReferencedEntityName(
value.getElement()
)
);
}
// Hibernate uses a middle table for mapping this relation, so we get it's name directly.
return value.getCollectionTable().getName();
}
@SuppressWarnings({"unchecked"})
private void addWithMiddleTable() {
LOG.debugf(
"Adding audit mapping for property %s.%s: collection with a join table",
referencingEntityName,
propertyName
);
// Generating the name of the middle table
String auditMiddleTableName;
String auditMiddleEntityName;
if ( !StringTools.isEmpty( propertyAuditingData.getJoinTable().name() ) ) {
auditMiddleTableName = propertyAuditingData.getJoinTable().name();
auditMiddleEntityName = propertyAuditingData.getJoinTable().name();
}
else {
final String middleTableName = getMiddleTableName( propertyValue, referencingEntityName );
auditMiddleTableName = mainGenerator.getVerEntCfg().getAuditTableName( null, middleTableName );
auditMiddleEntityName = mainGenerator.getVerEntCfg().getAuditEntityName( middleTableName );
}
LOG.debugf( "Using join table name: %s", auditMiddleTableName );
// Generating the XML mapping for the middle entity, only if the relation isn't inverse.
// If the relation is inverse, will be later checked by comparing middleEntityXml with null.
Element middleEntityXml;
if ( !propertyValue.isInverse() ) {
// Generating a unique middle entity name
auditMiddleEntityName = mainGenerator.getAuditEntityNameRegister().createUnique( auditMiddleEntityName );
// Registering the generated name
mainGenerator.getAuditEntityNameRegister().register( auditMiddleEntityName );
middleEntityXml = createMiddleEntityXml(
auditMiddleTableName,
auditMiddleEntityName,
propertyValue.getWhere()
);
}
else {
middleEntityXml = null;
}
// ******
// Generating the mapping for the referencing entity (it must be an entity).
// ******
// Getting the id-mapping data of the referencing entity (the entity that "owns" this collection).
final IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
// Only valid for an inverse relation; null otherwise.
String mappedBy;
// The referencing prefix is always for a related entity. So it has always the "_" at the end added.
String referencingPrefixRelated;
String referencedPrefix;
if ( propertyValue.isInverse() ) {
// If the relation is inverse, then referencedEntityName is not null.
mappedBy = getMappedBy(
propertyValue.getCollectionTable(),
mainGenerator.getMetadata().getEntityBinding( referencedEntityName )
);
referencingPrefixRelated = mappedBy + "_";
referencedPrefix = StringTools.getLastComponent( referencedEntityName );
}
else {
mappedBy = null;
referencingPrefixRelated = StringTools.getLastComponent( referencingEntityName ) + "_";
referencedPrefix = referencedEntityName == null ? "element" : propertyName;
}
// Storing the id data of the referencing entity: original mapper, prefixed mapper and entity name.
final MiddleIdData referencingIdData = createMiddleIdData(
referencingIdMapping,
referencingPrefixRelated,
referencingEntityName
);
// Creating a query generator builder, to which additional id data will be added, in case this collection
// references some entities (either from the element or index). At the end, this will be used to build
// a query generator to read the raw data collection from the middle table.
final QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder(
mainGenerator.getGlobalCfg(),
mainGenerator.getVerEntCfg(),
mainGenerator.getAuditStrategy(),
referencingIdData,
auditMiddleEntityName,
isRevisionTypeInId(),
propertyValue.getOrderBy() == null
? propertyValue.getManyToManyOrdering()
: propertyValue.getOrderBy()
);
// Adding the XML mapping for the referencing entity, if the relation isn't inverse.
if ( middleEntityXml != null ) {
// Adding related-entity (in this case: the referencing's entity id) id mapping to the xml.
addRelatedToXmlMapping(
middleEntityXml, referencingPrefixRelated,
MetadataTools.getColumnNameIterator( propertyValue.getKey().getColumnIterator() ),
referencingIdMapping
);
}
// ******
// Generating the element mapping.
// ******
final MiddleComponentData elementComponentData = addValueToMiddleTable(
propertyValue.getElement(),
middleEntityXml,
queryGeneratorBuilder,
referencedPrefix,
propertyAuditingData.getJoinTable().inverseJoinColumns(),
!isLobMapElementType()
);
// ******
// Generating the index mapping, if an index exists.
// ******
final MiddleComponentData indexComponentData = addIndex( middleEntityXml, queryGeneratorBuilder );
// ******
// Generating the property mapper.
// ******
// Building the query generator.
final RelationQueryGenerator queryGenerator = queryGeneratorBuilder.build( elementComponentData, indexComponentData );
// Creating common data
final CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
mainGenerator.getVerEntCfg(),
auditMiddleEntityName,
propertyAuditingData.getPropertyData(),
referencingIdData,
queryGenerator,
propertyValue.getRole()
);
// Checking the type of the collection and adding an appropriate mapper.
addMapper( commonCollectionMapperData, elementComponentData, indexComponentData );
// ******
// Storing information about this relation.
// ******
storeMiddleEntityRelationInformation( mappedBy );
}
private MiddleComponentData addIndex(Element middleEntityXml, QueryGeneratorBuilder queryGeneratorBuilder) {
if ( propertyValue instanceof IndexedCollection ) {
final IndexedCollection indexedValue = (IndexedCollection) propertyValue;
final String mapKey = propertyAuditingData.getMapKey();
if ( mapKey == null ) {
// This entity doesn't specify a javax.persistence.MapKey. Mapping it to the middle entity.
return addValueToMiddleTable(
indexedValue.getIndex(),
middleEntityXml,
queryGeneratorBuilder,
"mapkey",
null,
true
);
}
else {
final IdMappingData referencedIdMapping = mainGenerator.getEntitiesConfigurations()
.get( referencedEntityName ).getIdMappingData();
final int currentIndex = queryGeneratorBuilder == null ? 0 : queryGeneratorBuilder.getCurrentIndex();
if ( "".equals( mapKey ) ) {
// The key of the map is the id of the entity.
return new MiddleComponentData(
new MiddleMapKeyIdComponentMapper(
mainGenerator.getVerEntCfg(),
referencedIdMapping.getIdMapper()
),
currentIndex
);
}
else {
// The key of the map is a property of the entity.
return new MiddleComponentData(
new MiddleMapKeyPropertyComponentMapper(
mapKey,
propertyAuditingData.getAccessType()
),
currentIndex
);
}
}
}
else {
// No index - creating a dummy mapper.
return new MiddleComponentData( new MiddleDummyComponentMapper(), 0 );
}
}
/**
* @param value Value, which should be mapped to the middle-table, either as a relation to another entity,
* or as a simple value.
* @param xmlMapping If not null
, xml mapping for this value is added to this element.
* @param queryGeneratorBuilder In case value
is a relation to another entity, information about it
* should be added to the given.
* @param prefix Prefix for proeprty names of related entities identifiers.
* @param joinColumns Names of columns to use in the xml mapping, if this array isn't null and has any elements.
*
* @return Data for mapping this component.
*/
@SuppressWarnings({"unchecked"})
private MiddleComponentData addValueToMiddleTable(
Value value,
Element xmlMapping,
QueryGeneratorBuilder queryGeneratorBuilder,
String prefix,
JoinColumn[] joinColumns,
boolean key) {
final Type type = value.getType();
if ( type instanceof ManyToOneType ) {
final String prefixRelated = prefix + "_";
final String referencedEntityName = MappingTools.getReferencedEntityName( value );
final IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(
referencingEntityName,
referencedEntityName,
propertyAuditingData,
true
);
// Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the
// relation isn't inverse (so when xmlMapping
is not null).
if ( xmlMapping != null ) {
addRelatedToXmlMapping(
xmlMapping, prefixRelated,
joinColumns != null && joinColumns.length > 0
? MetadataTools.getColumnNameIterator( joinColumns )
: MetadataTools.getColumnNameIterator( value.getColumnIterator() ),
referencedIdMapping
);
}
// Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name.
final MiddleIdData referencedIdData = createMiddleIdData(
referencedIdMapping,
prefixRelated,
referencedEntityName
);
// And adding it to the generator builder.
queryGeneratorBuilder.addRelation( referencedIdData );
return new MiddleComponentData(
new MiddleRelatedComponentMapper( referencedIdData ),
queryGeneratorBuilder.getCurrentIndex()
);
}
else if ( type instanceof ComponentType ) {
// Collection of embeddable elements.
final Component component = (Component) value;
final Class componentClass = ReflectionTools.loadClass(
component.getComponentClassName(),
mainGenerator.getClassLoaderService()
);
final MiddleEmbeddableComponentMapper componentMapper = new MiddleEmbeddableComponentMapper(
new MultiPropertyMapper(),
componentClass
);
final Element parentXmlMapping = xmlMapping.getParent();
final ComponentAuditingData auditData = new ComponentAuditingData();
final ReflectionManager reflectionManager = mainGenerator
.getMetadata()
.getMetadataBuildingOptions()
.getReflectionManager();
new ComponentAuditedPropertiesReader(
ModificationStore.FULL,
new AuditedPropertiesReader.ComponentPropertiesSource( reflectionManager, component ),
auditData, mainGenerator.getGlobalCfg(), reflectionManager, ""
).read();
// Emulating first pass.
for ( String auditedPropertyName : auditData.getPropertyNames() ) {
final PropertyAuditingData nestedAuditingData = auditData.getPropertyAuditingData( auditedPropertyName );
mainGenerator.addValue(
parentXmlMapping,
component.getProperty( auditedPropertyName ).getValue(),
componentMapper,
prefix, xmlMappingData,
nestedAuditingData,
true,
true,
true
);
}
// Emulating second pass so that the relations can be mapped too.
for ( String auditedPropertyName : auditData.getPropertyNames() ) {
final PropertyAuditingData nestedAuditingData = auditData.getPropertyAuditingData( auditedPropertyName );
mainGenerator.addValue(
parentXmlMapping,
component.getProperty( auditedPropertyName ).getValue(),
componentMapper,
referencingEntityName,
xmlMappingData,
nestedAuditingData,
true,
false,
true
);
}
// Add an additional column holding a number to make each entry unique within the set.
// Embeddable properties may contain null values, so cannot be stored within composite primary key.
if ( propertyValue.isSet() ) {
final String setOrdinalPropertyName = mainGenerator.getVerEntCfg()
.getEmbeddableSetOrdinalPropertyName();
final Element ordinalProperty = MetadataTools.addProperty(
xmlMapping, setOrdinalPropertyName, "integer", true, true
);
MetadataTools.addColumn(
ordinalProperty, setOrdinalPropertyName, null, null, null, null, null, null, false
);
}
return new MiddleComponentData( componentMapper, 0 );
}
else {
// Last but one parameter: collection components are always insertable
final boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(
key ? xmlMapping : xmlMapping.getParent(),
new PropertyAuditingData(
prefix,
"field",
ModificationStore.FULL,
RelationTargetAuditMode.AUDITED,
null,
null,
false
),
value,
null,
true,
key
);
if ( mapped && key ) {
// Simple values are always stored in the first item of the array returned by the query generator.
return new MiddleComponentData(
new MiddleSimpleComponentMapper( mainGenerator.getVerEntCfg(), prefix ),
0
);
}
else if ( mapped && !key ) {
// when mapped but not part of the key, its stored as a dummy mapper??
return new MiddleComponentData(
new MiddleMapElementNotKeyComponentMapper( mainGenerator.getVerEntCfg(), prefix ),
0
);
}
else {
mainGenerator.throwUnsupportedTypeException( type, referencingEntityName, propertyName );
// Impossible to get here.
throw new AssertionError();
}
}
}
private void addMapper(
CommonCollectionMapperData commonCollectionMapperData,
MiddleComponentData elementComponentData,
MiddleComponentData indexComponentData) {
final Type type = propertyValue.getType();
final boolean embeddableElementType = isEmbeddableElementType();
final boolean lobMapElementType = isLobMapElementType();
if ( type instanceof SortedSetType ) {
currentMapper.addComposite(
propertyAuditingData.getPropertyData(),
new SortedSetCollectionMapper(
commonCollectionMapperData,
TreeSet.class,
SortedSetProxy.class,
elementComponentData,
propertyValue.getComparator(),
embeddableElementType,
embeddableElementType
)
);
}
else if ( type instanceof SetType ) {
currentMapper.addComposite(
propertyAuditingData.getPropertyData(),
new BasicCollectionMapper(
commonCollectionMapperData,
HashSet.class,
SetProxy.class,
elementComponentData,
embeddableElementType,
embeddableElementType
)
);
}
else if ( type instanceof SortedMapType ) {
// Indexed collection, so indexComponentData
is not null.
currentMapper.addComposite(
propertyAuditingData.getPropertyData(),
new SortedMapCollectionMapper(
commonCollectionMapperData,
TreeMap.class,
SortedMapProxy.class,
elementComponentData,
indexComponentData,
propertyValue.getComparator(),
embeddableElementType || lobMapElementType
)
);
}
else if ( type instanceof MapType ) {
// Indexed collection, so indexComponentData
is not null.
currentMapper.addComposite(
propertyAuditingData.getPropertyData(),
new MapCollectionMapper